diff --git a/CHANGELOG.md b/CHANGELOG.md index af0a73a..c0d0a6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to the Zlux App Server package will be documented in this fi ## v2.17.0 - Enhancement: app-server can now use Zowe's standardized and simplified AT-TLS configuration simply by toggling `zowe.network.server.tls.attls: true` or `components.app-server.zowe.network.server.tls.attls: true`. If you wish to control client tls separately from server tls, you can also use `zowe.network.client.tls.attls` or `components.app-server.zowe.network.client.tls.attls`. (#300) (#303) +- Enhancement: The app-server configure stage performance increased due to combining two seperate processes in this stage (plugins-init.js and initInstance.js) into one. (#304) - Enhancement: Remove dns check specific to node 14 and below to reduce startup time. Node 14 has not been supported since september 2023. (#304) ## v2.16.0 diff --git a/bin/configure.sh b/bin/configure.sh index 6baada1..0af66e4 100755 --- a/bin/configure.sh +++ b/bin/configure.sh @@ -46,12 +46,3 @@ fi . ./init/node-init.sh cd ../lib CONFIG_FILE=$ZWE_CLI_PARAMETER_CONFIG $NODE_BIN initInstance.js - -cd ${COMPONENT_HOME}/share/zlux-app-server/bin/init -if [ "${ZWE_components_app_server_zowe_useConfigmgr}" = "false" ]; then - . ./plugins-init.sh -elif [ "${ZWE_zowe_useConfigmgr}" = "true" ]; then - _CEE_RUNOPTS="XPLINK(ON),HEAPPOOLS(OFF)" ${ZWE_zowe_runtimeDirectory}/bin/utils/configmgr -script "${ZWE_zowe_runtimeDirectory}/components/app-server/share/zlux-app-server/bin/init/plugins-init.js" -else - . ./plugins-init.sh -fi diff --git a/bin/init/plugins-init.js b/bin/init/plugins-init.js deleted file mode 100644 index d99e7de..0000000 --- a/bin/init/plugins-init.js +++ /dev/null @@ -1,221 +0,0 @@ -/* - This program and the accompanying materials are - made available under the terms of the Eclipse Public License v2.0 which accompanies - this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html - - SPDX-License-Identifier: EPL-2.0 - - Copyright Contributors to the Zowe Project. -*/ - -import * as os from 'os'; -import * as zos from 'zos'; -import * as std from 'std'; -import * as xplatform from 'xplatform'; -import * as fs from '../../../../../../bin/libs/fs'; -import * as common from '../../../../../../bin/libs/common'; -import * as componentlib from '../../../../../../bin/libs/component'; -import { PathAPI as pathoid } from '../../../../../../bin/libs/pathoid'; - -common.printFormattedDebug("ZWED", "plugins-init", `Started plugins-init.js, platform=${os.platform}`); - -const runtimeDirectory=std.getenv('ZWE_zowe_runtimeDirectory'); -const extensionDirectory=std.getenv('ZWE_zowe_extensionDirectory'); -const workspaceDirectory=std.getenv('ZWE_zowe_workspaceDirectory'); -const recognizerDirectory=workspaceDirectory+'/app-server/ZLUX/pluginStorage/org.zowe.zlux.ng2desktop/recognizers'; -const actionsDirectory=workspaceDirectory+'/app-server/ZLUX/pluginStorage/org.zowe.zlux.ng2desktop/actions'; - -const installedComponentsEnv=std.getenv('ZWE_INSTALLED_COMPONENTS'); -const installedComponents = installedComponentsEnv ? installedComponentsEnv.split(',') : null; - -const enabledComponentsEnv=std.getenv('ZWE_ENABLED_COMPONENTS'); -const enabledComponents = enabledComponentsEnv ? enabledComponentsEnv.split(',') : null; - -const pluginPointerDirectory = `${workspaceDirectory}/app-server/plugins`; - -function deleteFile(path) { - return os.remove(path); -} - -function registerPlugin(pluginPath, pluginDefinition) { - const pointerPath = `${pluginPointerDirectory}/${pluginDefinition.identifier}.json`; - let location, relativeTo; - if (pluginPath.startsWith(runtimeDirectory)) { - relativeTo = "$ZWE_zowe_runtimeDirectory"; - location = pluginPath.substring(runtimeDirectory.length); - if (location.startsWith('/')) { - location = location.substring(1); - } - - xplatform.storeFileUTF8(pointerPath, xplatform.AUTO_DETECT, JSON.stringify({ - "identifier": pluginDefinition.identifier, - "pluginLocation": location, - "relativeTo": relativeTo - }, null, 2)); - } else { - xplatform.storeFileUTF8(pointerPath, xplatform.AUTO_DETECT, JSON.stringify({ - "identifier": pluginDefinition.identifier, - "pluginLocation": pluginPath - }, null, 2)); - } - registerApp2App(pluginPath, pluginDefinition.identifier, pluginDefinition.pluginVersion); -} - - - -function registerApp2App(pluginDirectory, pluginId, pluginVersion) { - common.printFormattedDebug("ZWED", "plugins-init", `app2app for ${pluginId}`); - copyRecognizers(pluginDirectory, pluginId, pluginVersion); - copyActions(pluginDirectory, pluginId, pluginVersion); -} - -function deregisterPlugin(pluginDefinition) { - const filePath = `${pluginPointerDirectory}/${pluginDefinition.identifier}.json`; - if (fs.fileExists(filePath, true)) { - const rc = deleteFile(filePath); - if (rc !== 0) { - common.printFormattedError("ZWED", "plugins-init", `Could not deregister plugin ${pluginDefinition.identifier}, delete ${filePath} failed, error=${rc}`); - } - return rc !== 0; - } else { - return deregisterApp2App(pluginDefinition.identifier); - } -} - -function deregisterApp2App(appId) { - const actionPath = pathoid.join(actionsDirectory, appId); - if (fs.fileExists(actionPath, true)) { - const rc = deleteFile(actionPath); - if (rc !== 0) { - common.printFormattedError("ZWED", "plugins-init", `Could not deregister plugin ${appId}, delete ${actionPath} failed, error=${rc}`); - } - return rc === 0; - } - //TODO how to deregister recognizer? -} - -function copyRecognizers(appDir, appId, appVers) { - let recognizers; - let recognizersKeys; - let configRecognizers; - const pluginRecognizersLocation = pathoid.join(appDir, "config", "recognizers"); - - - if (fs.directoryExists(pluginRecognizersLocation)) { // Get recognizers in a plugin's appDir/config/xxx location - common.printFormattedDebug("ZWED", "plugins-init", `rec ${pluginRecognizersLocation} exists`); - fs.getFilesInDirectory(pluginRecognizersLocation).forEach(filename => { - const filepath = pathoid.resolve(pluginRecognizersLocation, filename); - const filepathConfig = pathoid.resolve(pathoid.join(recognizerDirectory, filename)); - - recognizers = JSON.parse(xplatform.loadFileUTF8(filepath, xplatform.AUTO_DETECT)).recognizers; - recognizersKeys = Object.keys(recognizers) - for (const key of recognizersKeys) { // Add metadata for plugin version & plugin identifier of origin (though objects don't have to be plugin specific) - recognizers[key].pluginVersion = appVers; - recognizers[key].pluginIdentifier = appId; - recognizers[key].key = appId + ":" + key + ":" + recognizers[key].id; // pluginid_that_provided_it:index(or_name)_in_that_provider:actionid - } - common.printFormattedDebug("ZWED", "plugins-init", `ZWED0301I Found ${filepath} in config for '${appId}'`); - common.printFormattedDebug("ZWED", "plugins-init", `Going to merge into ${filepathConfig}`); - try { // Get pre-existing recognizers in config, if any - configRecognizers = fs.fileExists(filepathConfig) ? JSON.parse(xplatform.loadFileUTF8(filepathConfig, xplatform.AUTO_DETECT)).recognizers : {}; - const configRecognizersKeys = Object.keys(configRecognizers); - for (const configKey of configRecognizersKeys) { // Traverse config recognizers - for (const key of recognizersKeys) { // Traverse plugin recognizers - if (configRecognizers[configKey].key && recognizers[key].key && configRecognizers[configKey].key == recognizers[key].key) { // TODO: Need to implement real keys for Recognizers - configRecognizers[configKey] = recognizers[key]; // Choose the recognizers originating from plugin - } - } - } - recognizers = Object.assign(configRecognizers, recognizers); // If found, combine the ones found in config with ones found in plugin - } catch (e) { - common.printFormattedError("ZWED", "plugins-init", `Error: Invalid JSON for ${filepathConfig}`); - } - - if (recognizers) { // Attempt to copy recognizers over to config location for Desktop access later - try { //TODO: Doing recognizers.recognizers is redundant. We may want to consider refactoring in the future - xplatform.storeFileUTF8(filepathConfig, xplatform.AUTO_DETECT, '{ "recognizers":' + JSON.stringify(recognizers) + '}'); - common.printFormattedInfo("ZWED", "plugins-init", "ZWED0294I Successfully loaded " + Object.keys(recognizers).length + " recognizers for '" + appId + "' into config at "+filepathConfig); - } catch (e) { - common.printFormattedError("ZWED", "plugins-init", `ZWED0177W Unable to load ${recognizers} for '${appId}' into config`); - } - } - - }); - } - common.printFormattedDebug("ZWED", "plugins-init", `Done rec`); -} - -function copyActions(appDir, appId, appVers) { - let actions; - let actionsKeys; - const pluginActionsLocation = pathoid.join(appDir, "config", "actions", appId); - - if (fs.fileExists(pluginActionsLocation)) { - common.printFormattedDebug("ZWED", "plugins-init", `act ${pluginActionsLocation} exists`); - try { // Get actions in a plugin's appDir/config/xxx location - actions = JSON.parse(xplatform.loadFileUTF8(pluginActionsLocation, xplatform.AUTO_DETECT)).actions; - actionsKeys = Object.keys(actions) - for (const key of actionsKeys) { // Add metadata for plugin version & plugin identifier of origin (though objects don't have to be plugin specific) - actions[key].pluginVersion = appVers; - actions[key].pluginIdentifier = appId; - } - common.printFormattedDebug("ZWED", "plugins-init", `ZWED0301I Found ${actions} in config for '${appId}'`); - } catch (e) { - common.printFormattedError("ZWED", "plugins-init", `Error: Malformed JSON in ${pluginActionsLocation}`); - } - - if (actions) { // Attempt to copy actions over to config location for Desktop access later - try { //TODO: Doing actions.actions is redundant. We may want to consider refactoring in the future - xplatform.storeFileUTF8(pathoid.join(actionsDirectory, appId), xplatform.AUTO_DETECT, '{ "actions":' + JSON.stringify(actions) + '}'); - common.printFormattedInfo("ZWED", "plugins-init", "ZWED0295I Successfully loaded " + actions.length + " actions for '" + appId + "' into config at "+pathoid.join(actionsDirectory, appId)); - } catch (e) { - common.printFormattedError("ZWED", "plugins-init", `ZWED0177W Unable to load ${actions} for '${appId}' into config`); - } - } - } - common.printFormattedDebug("ZWED", "plugins-init", `done act`); -} - - -if (!fs.directoryExists(pluginPointerDirectory, true)) { - const rc = os.mkdir(pluginPointerDirectory, 0o770); - if (rc < 0) { - common.printFormattedError("ZWED", "plugins-init", `Could not create pluginsDir=${pluginPointerDirectory}, err=${rc}`); - std.exit(2); - } -} - -common.printFormattedDebug("ZWED", "plugins-init", "Start iteration"); - -//A port of https://github.com/zowe/zlux-app-server/blob/v2.x/staging/bin/init/plugins-init.sh - -installedComponents.forEach(function(installedComponent) { - const componentDirectory = componentlib.findComponentDirectory(installedComponent); - if (componentDirectory) { - const enabled = enabledComponents.includes(installedComponent); - common.printFormattedDebug("ZWED", "plugins-init", `Checking plugins for component=${installedComponent}, enabled=${enabled}`); - - const manifest = componentlib.getManifest(componentDirectory); - if (manifest.appfwPlugins) { - manifest.appfwPlugins.forEach(function (manifestPluginRef) { - const path = manifestPluginRef.path; - const fullPath = `${componentDirectory}/${path}` - const pluginDefinition = componentlib.getPluginDefinition(fullPath); - if (pluginDefinition) { - if (enabled) { - common.printFormattedInfo("ZWED", "plugins-init", `Registering plugin ${fullPath}`); - registerPlugin(fullPath, pluginDefinition); - } else { - common.printFormattedDebug("ZWED", "plugins-init", `Deregistering plugin ${fullPath}`); - deregisterPlugin(pluginDefinition); - } - } else { - common.printFormattedError("ZWED", "plugins-init", `Skipping plugin at ${fullPath} due to pluginDefinition missing or invalid`); - } - }); - } - } else { - common.printFormattedError("ZWED", "plugins-init", `Warning: Could not remove app framework plugins for extension ${installedComponent} because its directory could not be found within ${extensionDirectory}`); - } -}); - diff --git a/bin/init/plugins-init.sh b/bin/init/plugins-init.sh deleted file mode 100644 index 24c0d41..0000000 --- a/bin/init/plugins-init.sh +++ /dev/null @@ -1,51 +0,0 @@ -# $1=whether to use nodejs or not for installing (affects app2app installation) -# $2=whether to check pc bit of zss services - -# ZWE_zowe_runtimeDirectory -# ZWE_zowe_extensionDirectory -# ZWE_INSTALLED_COMPONENTS=api-catalog,apiml-common-lib,app-server,caching-service,common-java-lib,discovery,explorer-jes,explorer-mvs,explorer-uss,files-api,gateway,jobs-api,launcher,metrics-service,zss,process-manager -# ZWE_ENABLED_COMPONENTS=api-catalog,app-server,caching-service,discovery,explorer-jes,explorer-mvs,explorer-uss,gateway,zss - -echo "Using runtime=${ZWE_zowe_runtimeDirectory} and extensions=${ZWE_zowe_extensionDirectory}" -echo "Checking installed=${ZWE_INSTALLED_COMPONENTS}" -echo "Checking enabled=${ZWE_ENABLED_COMPONENTS}" - -. ../utils/plugin-utils.sh - -plugins_dir=$(getPluginsDir) - -for installed_component in $(echo "${ZWE_INSTALLED_COMPONENTS}" | sed "s/,/ /g"); do - extension_path=$(find_component_directory ${installed_component}) - if [ -d "${extension_path}" ]; then - is_enabled=false - for enabled_component in $(echo "${ZWE_ENABLED_COMPONENTS}" | sed "s/,/ /g"); do - if [ "${enabled_component}" = "${installed_component}" ]; then - is_enabled=true - fi - done - - echo "Checking plugins for component=${installed_component}, enabled=${is_enabled}" - - # HERE: can we do this without any nodejs? probably no, but lets use the zowe install packaging utils. - # init-plugins.js $is_enabled "${extension_path}" - - iterator=0 - plugin_folder=$(read_component_manifest "${extension_path}" .appfwPlugins.[${iterator}].path) - while [ -n "${plugin_folder}" ]; do - fullpath="$extension_path/${plugin_folder}" - if [ "$is_enabled" = "true" ]; then - echo "Registering plugin ${fullpath}" - # NOTE: relativeTo does not need to be handled here because this process occurs every start so the results should be "portable" by update on restart - - INSTALL_NO_NODE=$1 ../install-app.sh "$fullpath" "${plugins_dir}" - else - echo "Deregistering plugin ${fullpath}" - ../uninstall-app.sh "$fullpath" "${plugins_dir}" - fi - iterator=`expr $iterator + 1` - plugin_folder=$(read_component_manifest "${extension_path}" .appfwPlugins.[${iterator}].path) - done - else - echo "Warning: Could not remove app framework plugins for extension ${installed_component} because its directory could not be found within ${ZWE_zowe_extensionDirectory}" - fi -done diff --git a/lib/initInstance.js b/lib/initInstance.js index e9bed84..04080d9 100644 --- a/lib/initInstance.js +++ b/lib/initInstance.js @@ -10,6 +10,7 @@ const fs = require('fs'); const path = require('path'); +const YAML = require('yaml'); const argParser = require('../../zlux-server-framework/utils/argumentParser'); const mergeUtils = require('../../zlux-server-framework/utils/mergeUtils'); const yamlConfig = require('../../zlux-server-framework/utils/yamlConfig'); @@ -18,6 +19,7 @@ const initUtils = require('./initUtils'); const os = require('os'); const { execSync } = require('child_process'); +initUtils.printFormattedDebug(`Started initInstance.js, platform=${os.platform()}`); const haInstanceId = yamlConfig.getCurrentHaInstanceId(); let config = {}; @@ -42,43 +44,43 @@ const versionLocation = path.join(destination, 'component.json'); config.productDir = path.join(__dirname, '..', 'defaults'); //Begin generate any missing folders -fs.mkdirSync(destination, {mode: initUtils.FOLDER_MODE, recursive: true}); +initUtils.mkdirp(destination, initUtils.FOLDER_MODE); if (!config.siteDir) { config.siteDir = path.join(destination, 'site'); } const sitePluginStorage = path.join(config.siteDir, 'ZLUX', 'pluginStorage'); -fs.mkdirSync(sitePluginStorage, {mode: initUtils.FOLDER_MODE, recursive: true}); +initUtils.mkdirp(sitePluginStorage, initUtils.FOLDER_MODE); if (!config.instanceDir) { config.instanceDir = destination; } const instancePluginStorage = path.join(config.instanceDir, 'ZLUX', 'pluginStorage'); -fs.mkdirSync(instancePluginStorage, {mode: initUtils.FOLDER_MODE, recursive: true}); +initUtils.mkdirp(instancePluginStorage, initUtils.FOLDER_MODE); const recognizersPluginStorage = path.join(config.instanceDir, 'ZLUX/pluginStorage', 'org.zowe.zlux.ng2desktop/recognizers'); -fs.mkdirSync(recognizersPluginStorage, {mode: initUtils.FOLDER_MODE, recursive: true}); +initUtils.mkdirp(recognizersPluginStorage, initUtils.FOLDER_MODE); const actionsPluginStorage = path.join(config.instanceDir, 'ZLUX/pluginStorage/org.zowe.zlux.ng2desktop', 'actions'); -fs.mkdirSync(actionsPluginStorage, {mode: initUtils.FOLDER_MODE, recursive: true}); +initUtils.mkdirp(actionsPluginStorage, initUtils.FOLDER_MODE); const instanceConfig = path.join(config.instanceDir, 'serverConfig'); //750 specifically, to keep server config secure -fs.mkdirSync(instanceConfig, {mode: 0o0750}); +initUtils.mkdirp(instanceConfig, 0o0750); if (!config.groupsDir) { config.groupsDir = path.join(config.instanceDir, 'groups'); } -fs.mkdirSync(config.groupsDir, {mode: initUtils.FOLDER_MODE, recursive: true}); +initUtils.mkdirp(config.groupsDir, initUtils.FOLDER_MODE); if (!config.usersDir) { config.usersDir = path.join(config.instanceDir, 'users'); } -fs.mkdirSync(config.usersDir, {mode: initUtils.FOLDER_MODE, recursive: true}); +initUtils.mkdirp(config.usersDir, initUtils.FOLDER_MODE); if (!config.pluginsDir) { config.pluginsDir = path.join(destination, 'plugins'); } -fs.mkdirSync(config.pluginsDir, {mode: initUtils.FOLDER_MODE, recursive: true}); +initUtils.mkdirp(config.pluginsDir, initUtils.FOLDER_MODE); function generateComponentJson() { let componentJsonContent; @@ -148,13 +150,11 @@ if (siteStorage.length == 0 && instanceStorage.length == 0) { process.exit(1); } generateComponentJson() - process.exit(0); }); } else { execSync("cp -r "+path.join(config.productDir, 'ZLUX', 'pluginStorage')+" "+path.join(config.instanceDir, 'ZLUX')); execSync("chmod -R 770 "+instancePluginStorage); generateComponentJson() - process.exit(0); } } @@ -198,3 +198,47 @@ if (siteStorage.length == 0 && instanceStorage.length == 0) { // } catch (e) { // //skip process // } + +const RUNTIME_DIRECTORY=process.env.ZWE_zowe_runtimeDirectory; +const EXTENSION_DIRECTORY=process.env.ZWE_zowe_extensionDirectory; + +const INSTALLED_COMPONENTS_ENV=process.env.ZWE_INSTALLED_COMPONENTS; +const INSTALLED_COMPONENTS = INSTALLED_COMPONENTS_ENV ? INSTALLED_COMPONENTS_ENV.split(',') : []; + +const ENABLED_COMPONENTS_ENV=process.env.ZWE_ENABLED_COMPONENTS; +const ENABLED_COMPONENTS = ENABLED_COMPONENTS_ENV ? ENABLED_COMPONENTS_ENV.split(',') : []; + + +initUtils.printFormattedDebug("Start component iteration"); + +INSTALLED_COMPONENTS.forEach(function(installedComponent) { + const componentDirectory = initUtils.findComponentDirectory(RUNTIME_DIRECTORY, EXTENSION_DIRECTORY, installedComponent); + if (componentDirectory) { + const enabled = ENABLED_COMPONENTS.includes(installedComponent); + initUtils.printFormattedDebug(`Checking plugins for component=${installedComponent}, enabled=${enabled}`); + + const manifest = YAML.parse(fs.readFileSync(initUtils.getManifestPath(componentDirectory), 'utf8')); + if (manifest.appfwPlugins) { + manifest.appfwPlugins.forEach(function (manifestPluginRef) { + const path = manifestPluginRef.path; + const fullPath = `${componentDirectory}/${path}` + const pluginDefinition = `${fullPath}/pluginDefinition.json`; + if (pluginDefinition && initUtils.fileExists(pluginDefinition)) { + const pluginDefinitionJson = JSON.parse(fs.readFileSync(pluginDefinition, 'utf8')); + if (enabled) { + initUtils.printFormattedInfo(`Registering plugin ${fullPath}`); + initUtils.registerPlugin(fullPath, pluginDefinitionJson, config.pluginsDir, actionsPluginStorage, recognizersPluginStorage, RUNTIME_DIRECTORY); + } else { + initUtils.printFormattedDebug(`Deregistering plugin ${fullPath}`); + initUtils.deregisterPlugin(pluginDefinitionJson, config.pluginsDir, actionsPluginStorage); + } + } else { + initUtils.printFormattedError(`Skipping plugin at ${fullPath} due to pluginDefinition missing or invalid`); + } + }); + } + } else { + initUtils.printFormattedError(`Warning: Could not remove app framework plugins for extension ${installedComponent} because its directory could not be found within ${EXTENSION_DIRECTORY}`); + } +}); + diff --git a/lib/initUtils.js b/lib/initUtils.js index 8399c41..3fc7f57 100644 --- a/lib/initUtils.js +++ b/lib/initUtils.js @@ -9,12 +9,18 @@ */ const fs = require('fs'); +const os = require('os'); const path = require('path'); const ZLUX_ROOT_DIR = path.join(__dirname, '..'); const DEFAULT_PLUGINS_DIR = path.join(ZLUX_ROOT_DIR, 'defaults', 'plugins'); -module.exports.FOLDER_MODE = 0o0770; -module.exports.FILE_MODE = 0o0770; +const USERNAME = os.userInfo().username; +const LOG_LEVEL = process.env.ZWE_PRIVATE_LOG_LEVEL_ZWELS; +const PRINT_DEBUG = LOG_LEVEL == 'DEBUG' || LOG_LEVEL == 'TRACE'; +const FILE_MODE = 0o770; +const FOLDER_MODE = 0o770; +module.exports.FOLDER_MODE = FOLDER_MODE; +module.exports.FILE_MODE = FILE_MODE; module.exports.registerBundledPlugins = function(destination, configDestination, oldPlugins, filePermission) { @@ -25,6 +31,246 @@ module.exports.registerBundledPlugins = function(destination, configDestination, }); } +function mkdirp(directory, mode) { + try { + fs.mkdirSync(directory, {mode: mode, recursive: true}); + } catch (e) { + if (e.code != 'EEXIST') { + throw e; + } + } +} +module.exports.mkdirp = mkdirp; + + +function printFormattedMessage(level, message) { + var d = new Date(); +// d.setTime(d.getTime()-Logger.offsetMs); + var dateString = d.toISOString(); + dateString = dateString.substring(0,dateString.length-1).replace('T',' '); + console.log(`${dateString} ${USERNAME} ${level} (plugins-init) ${message}`); +} + +function printFormattedError(message) { + printFormattedMessage('ERROR', message); +} +module.exports.printFormattedError = printFormattedError; + +function printFormattedInfo(message) { + printFormattedMessage('INFO', message); +} +module.exports.printFormattedInfo = printFormattedInfo; + + +function printFormattedDebug(message) { + if (PRINT_DEBUG) { + printFormattedMessage('DEBUG', message); + } +} +module.exports.printFormattedDebug = printFormattedDebug; + +function getManifestPath(componentDir) { + if (fileExists(`${componentDir}/manifest.yaml`)) { + return `${componentDir}/manifest.yaml`; + } else if (fileExists(`${componentDir}/manifest.yml`)) { + return `${componentDir}/manifest.yml`; + } else if (fileExists(`${componentDir}/manifest.yaml`)) { + return `${componentDir}/manifest.json`; + } + return undefined; +} +module.exports.getManifestPath = getManifestPath; + +function findComponentDirectory(runtimeDirectory, extensionDirectory, componentId) { + if (directoryExists(`${runtimeDirectory}/components/${componentId}`)) { + return `${runtimeDirectory}/components/${componentId}`; + } else if (extensionDirectory && directoryExists(`${extensionDirectory}/${componentId}`)) { + return `${extensionDirectory}/${componentId}`; + } + return undefined; +} +module.exports.findComponentDirectory = findComponentDirectory; + + +function directoryExists(directory) { + try { + let stat = fs.statSync(directory); + if (stat) { + return stat.isDirectory(); + } else { + return false; + } + } catch (e) { + return false; + } +} +module.exports.directoryExits = directoryExists; + +function fileExists(file) { + try { + let stat = fs.statSync(file); + if (stat) { + return !stat.isDirectory(); + } else { + return false; + } + } catch (e) { + return false; + } +} +module.exports.fileExists = fileExists; + + +function deregisterPlugin(pluginDefinition, pluginPointerDirectory, actionsDirectory) { + const filePath = `${pluginPointerDirectory}/${pluginDefinition.identifier}.json`; + if (fileExists(filePath, true)) { + try { + fs.unlinkSync(filePath); + } catch (e) { + printFormattedError(`Could not deregister plugin ${pluginDefinition.identifier}, delete ${filePath} failed, error=${e}`); + } + return true; + } else { + return deregisterApp2App(pluginDefinition.identifier, actionsDirectory); + } +} +module.exports.deregisterPlugin = deregisterPlugin; + +function deregisterApp2App(appId, actionsDirectory) { + const actionPath = path.join(actionsDirectory, appId); + if (fileExists(actionPath, true)) { + try { + fs.unlinkSync(actionPath); + } catch (e) { + printFormattedError(`Could not deregister plugin ${appId}, delete ${actionPath} failed, error=${e}`); + } + return true; + } + //TODO how to deregister recognizer? +} +module.exports.deregisterApp2App = deregisterApp2App; + + + +function registerApp2App(pluginDirectory, pluginId, pluginVersion, pluginActionsLocation, pluginRecognizersLocation) { + printFormattedDebug(`app2app for ${pluginId}`); + copyRecognizers(pluginDirectory, pluginId, pluginVersion, pluginRecognizersLocation); + copyActions(pluginDirectory, pluginId, pluginVersion, pluginActionsLocation); +} +module.exports.registerApp2App = registerApp2App; + +function copyRecognizers(appDir, appId, appVers, recognizerDirectory) { + let recognizers; + let recognizersKeys; + let configRecognizers; + const pluginRecognizersLocation = path.join(appDir, "config", "recognizers"); + + + if (directoryExists(pluginRecognizersLocation)) { // Get recognizers in a plugin's appDir/config/xxx location + printFormattedDebug(`rec ${pluginRecognizersLocation} exists`); + fs.readdirSync(pluginRecognizersLocation, {withFileTypes: true}).filter(statObj=> statObj.isFile()) + .forEach((statObj) => { + const filename = statObj.name; + const filepath = path.resolve(pluginRecognizersLocation, filename); + const filepathConfig = path.resolve(path.join(recognizerDirectory, filename)); + + recognizers = JSON.parse(fs.readFileSync(filepath, 'utf8')).recognizers; + recognizersKeys = Object.keys(recognizers) + for (const key of recognizersKeys) { // Add metadata for plugin version & plugin identifier of origin (though objects don't have to be plugin specific) + recognizers[key].pluginVersion = appVers; + recognizers[key].pluginIdentifier = appId; + recognizers[key].key = appId + ":" + key + ":" + recognizers[key].id; // pluginid_that_provided_it:index(or_name)_in_that_provider:actionid + } + printFormattedDebug(`ZWED0301I Found ${filepath} in config for '${appId}'`); + printFormattedDebug(`Going to merge into ${filepathConfig}`); + try { // Get pre-existing recognizers in config, if any + configRecognizers = fileExists(filepathConfig) ? JSON.parse(fs.readFileSync(filepathConfig, 'utf8')).recognizers : {}; + const configRecognizersKeys = Object.keys(configRecognizers); + for (const configKey of configRecognizersKeys) { // Traverse config recognizers + for (const key of recognizersKeys) { // Traverse plugin recognizers + if (configRecognizers[configKey].key && recognizers[key].key && configRecognizers[configKey].key == recognizers[key].key) { // TODO: Need to implement real keys for Recognizers + configRecognizers[configKey] = recognizers[key]; // Choose the recognizers originating from plugin + } + } + } + recognizers = Object.assign(configRecognizers, recognizers); // If found, combine the ones found in config with ones found in plugin + } catch (e) { + printFormattedError(`Error: Invalid JSON for ${filepathConfig}`); + } + + if (recognizers) { // Attempt to copy recognizers over to config location for Desktop access later + try { //TODO: Doing recognizers.recognizers is redundant. We may want to consider refactoring in the future + fs.writeFileSync(filepathConfig, '{ "recognizers":' + JSON.stringify(recognizers) + '}', + {encoding: 'utf8', mode: FILE_MODE}); + console.log("ZWED", "plugins-init", "ZWED0294I Successfully loaded " + Object.keys(recognizers).length + " recognizers for '" + appId + "' into config at "+filepathConfig); + } catch (e) { + printFormattedError(`ZWED0177W Unable to load ${recognizers} for '${appId}' into config`); + } + } + + }); + } + printFormattedDebug(`Done rec`); +} + +function copyActions(appDir, appId, appVers, actionsDirectory) { + let actions; + let actionsKeys; + const pluginActionsLocation = path.join(appDir, "config", "actions", appId); + + if (fileExists(pluginActionsLocation)) { + printFormattedDebug(`act ${pluginActionsLocation} exists`); + try { // Get actions in a plugin's appDir/config/xxx location + actions = JSON.parse(fs.readFileSync(pluginActionsLocation, 'utf8')).actions; + actionsKeys = Object.keys(actions) + for (const key of actionsKeys) { // Add metadata for plugin version & plugin identifier of origin (though objects don't have to be plugin specific) + actions[key].pluginVersion = appVers; + actions[key].pluginIdentifier = appId; + } + printFormattedDebug(`ZWED0301I Found ${actions} in config for '${appId}'`); + } catch (e) { + printFormattedError(`Error: Malformed JSON in ${pluginActionsLocation}`); + } + + if (actions) { // Attempt to copy actions over to config location for Desktop access later + try { //TODO: Doing actions.actions is redundant. We may want to consider refactoring in the future + fs.writeFileSync(path.join(actionsDirectory, appId), '{ "actions":' + JSON.stringify(actions) + '}', + {encoding: 'utf8', mode: FILE_MODE}); + printFormattedInfo("ZWED0295I Successfully loaded " + actions.length + " actions for '" + appId + "' into config at "+path.join(actionsDirectory, appId)); + } catch (e) { + printFormattedError(`ZWED0177W Unable to load ${actions} for '${appId}' into config`); + } + } + } + printFormattedDebug(`done act`); +} + + +function registerPlugin(pluginPath, pluginDefinition, pluginPointerDirectory, pluginActionsLocation, pluginRecognizersLocation, runtimeDirectory) { + const pointerPath = `${pluginPointerDirectory}/${pluginDefinition.identifier}.json`; + let location, relativeTo; + if (pluginPath.startsWith(runtimeDirectory)) { + relativeTo = "$ZWE_zowe_runtimeDirectory"; + location = pluginPath.substring(runtimeDirectory.length); + if (location.startsWith('/')) { + location = location.substring(1); + } + + fs.writeFileSync(pointerPath, JSON.stringify({ + "identifier": pluginDefinition.identifier, + "pluginLocation": location, + "relativeTo": relativeTo + }, null, 2), {encoding: 'utf8', mode: FILE_MODE}); + } else { + fs.writeFileSync(pointerPath, JSON.stringify({ + "identifier": pluginDefinition.identifier, + "pluginLocation": pluginPath + }, null, 2), {encoding: 'utf8', mode: FILE_MODE}); + } + registerApp2App(pluginPath, pluginDefinition.identifier, pluginDefinition.pluginVersion, pluginActionsLocation, pluginRecognizersLocation); +} +module.exports.registerPlugin = registerPlugin; + /** @param pluginID Identifier of a plugin @param outputDir plugins directory where plugin identifier file will be placed @@ -77,7 +323,7 @@ module.exports.registerBundledPlugin = registerBundledPlugin; module.exports.setTerminalDefaults = function(configDestination, instanceItems) { if (instanceItems.indexOf('org.zowe.terminal.vt.json') != -1) { let defaultConfigDir = path.join(configDestination,'org.zowe.terminal.vt','sessions'); - fs.mkdirSync(defaultConfigDir, {recursive: true}); + mkdirp(defaultConfigDir); try { fs.writeFileSync(path.join(defaultConfigDir,'_defaultVT.json'), JSON.stringify({host:process.env['ZWED_SSH_HOST'] ? process.env['ZWED_SSH_HOST'] : "", @@ -93,7 +339,7 @@ module.exports.setTerminalDefaults = function(configDestination, instanceItems) security = process.env['ZWED_TN3270_SECURITY']; } let defaultConfigDir = path.join(configDestination,'org.zowe.terminal.tn3270','sessions'); - fs.mkdirSync(defaultConfigDir, {recursive: true}); + mkdirp(defaultConfigDir); try { let tn3270Json = {host:process.env['ZWED_TN3270_HOST'] ? process.env['ZWED_TN3270_HOST'] : "", port: process.env['ZWED_TN3270_PORT'] ? process.env['ZWED_TN3270_PORT'] : 23, diff --git a/package-lock.json b/package-lock.json index e22b38c..c57f16e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,64 @@ "name": "zlux-app-server", "version": "1.0.0", "dependencies": { + "yaml": "~2.4.5", "zlux-server-framework": "file:../zlux-server-framework" } }, - "../zlux-server-framework": {}, + "../zlux-server-framework": { + "version": "0.0.0-zlux.version.replacement", + "license": "EPL-2.0", + "dependencies": { + "@rocketsoftware/eureka-js-client": "~4.5.7", + "@rocketsoftware/express-ws": "^5.0.0", + "accept-language-parser": "~1.5.0", + "axios": "^1.6.8", + "bluebird": "3.7.2", + "body-parser": "~1.20.2", + "cookie-parser": "~1.4.6", + "diffie-hellman": "^5.0.3", + "express": "~4.19.2", + "express-session": "~1.18.0", + "express-static-gzip": "~2.1.7", + "graceful-fs": "~4.2.11", + "ipaddr.js": "~2.1.0", + "node-forge": "~1.3.1", + "normalize-url": "~7.0.0", + "require-from-string": "~2.0.2", + "semver": "~7.6.0", + "swagger-parser": "~10.0.3", + "ws": "^6.2.2", + "yaml": "~2.4.1", + "yauzl": "~3.1.2" + }, + "devDependencies": { + "@types/connect": "3.4.35", + "@types/express": "4.17.17", + "@types/express-serve-static-core": "4.17.35", + "@types/mime": "3.0.1", + "@types/node": "~16.18.0", + "@types/qs": "6.9.3", + "chai": "~4.2.0", + "chai-http": "~4.2.0", + "mocha": "~10.2.0", + "typescript": "~5.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2", + "keyring_js": "~1.1.0" + } + }, + "node_modules/yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/zlux-server-framework": { "resolved": "../zlux-server-framework", "link": true diff --git a/package.json b/package.json index f443f99..e86efb5 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "test": "echo \"Warning: no test specified\" && exit 0" }, "dependencies": { + "yaml": "~2.4.5", "zlux-server-framework": "file:../zlux-server-framework" } }