diff --git a/.github/workflows/build-core.yml b/.github/workflows/build-core.yml index 39e85fa4..e45cbd1e 100644 --- a/.github/workflows/build-core.yml +++ b/.github/workflows/build-core.yml @@ -2,7 +2,9 @@ name: Build Core on: push: branches: - - v2.x/staging + - v3.x/staging + - v3.x/rc + - v3.x/master pull_request: types: [opened, reopened, synchronize] @@ -28,9 +30,9 @@ on: description: 'zlux-shared PR number' required: false DEFAULT_BRANCH: - description: 'please enter the default branch you would like to build with, default will be v2.x/staging' + description: 'please enter the default branch you would like to build with, default will be v3.x/staging' required: false - default: 'v2.x/staging' + default: 'v3.x/staging' jobs: check-permission: @@ -154,9 +156,9 @@ jobs: ${{ runner.os }}-build-cache-node-modules- - name: '[Prep 2] Setup Node' - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: - node-version: 16.15.0 + node-version: 18 - name: '[Prep 3] Setup jFrog CLI' uses: jfrog/setup-jfrog-cli@v2 @@ -164,7 +166,7 @@ jobs: JF_ARTIFACTORY_1: ${{ secrets.JF_ARTIFACTORY_TOKEN }} - name: '[Prep 4] prepare workflow' - uses: zowe-actions/zlux-builds/core/prepare@v2.x/main + uses: zowe-actions/zlux-builds/core/prepare@v3.x/main with: github-user: ${{ secrets.ZOWE_ROBOT_USER }} github-password: ${{ secrets.ZOWE_ROBOT_TOKEN }} @@ -173,7 +175,7 @@ jobs: default-base: ${{ github.event.inputs.DEFAULT_BRANCH }} - name: '[Prep 5] build' - uses: zowe-actions/zlux-builds/core/build@v2.x/main + uses: zowe-actions/zlux-builds/core/build@v3.x/main with: zlux-app-manager: ${{ github.event.inputs.ZLUX_APP_MANAGER }} zlux-app-server: ${{ github.event.inputs.ZLUX_APP_SERVER }} @@ -183,12 +185,12 @@ jobs: zlux-shared: ${{ github.event.inputs.ZLUX_SHARED }} - name: '[Prep 6] packaging' - uses: zowe-actions/zlux-builds/core/package@v2.x/main + uses: zowe-actions/zlux-builds/core/package@v3.x/main with: pax-ssh-username: ${{ secrets.SSH_MARIST_USERNAME }} pax-ssh-password: ${{ secrets.SSH_MARIST_RACF_PASSWORD }} pax-name: zlux-core - name: '[Prep 7] deploy' - uses: zowe-actions/zlux-builds/core/deploy@v2.x/main + uses: zowe-actions/zlux-builds/core/deploy@v3.x/main diff --git a/CHANGELOG.md b/CHANGELOG.md index b5cb6756..cac3d004 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,27 @@ # Zlux App Server Changelog All notable changes to the Zlux App Server package will be documented in this file. - + +## v3.0.0 +- Enhancement: Added new plugin identifier for the V3 Ivy Desktop to list of default plugins. Also added this plugin to list of allowedPlugins.json. +- Enhancement: Updated schema to document new entry points in V3. + +## 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 +- Bugfix: Removed message saying node not found prior to discovery of node. Now, you will only get an error message if node is not found after lookup in NODE_HOME. + +## v2.14.0 +- Enhancement: Updated schema to specify multiple discovery servers. + +## v2.13.0 +- Enhancement: Updated schema to allow cipher customization in IANA format. (#284) +- Enhancement: Updated schema to allow curve customization. (#284) +- Enhancement: Updated defaults to read TLS settings and IP settings from the "zowe.network.server" attribute of Zowe.yaml. (#284) + ## v2.12.0 - enhancement: new versions of components can change the location of their plugins, as the app-server will now re-inspect the plugin locations on each startup. (#280) - bugfix: Removed error message "components/app-server/bin/configure.sh 26: .: FSUM6807 expression syntax error" seen in startup of Zowe in v2.11.0, caused by incorrect shell syntax. (#283) diff --git a/README.md b/README.md index a503e9bd..91aaa097 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ To build the App Server and Apps, the following is required: * **NodeJS** - v14.x minimum (except v14.17.2) up to v16.x +Note & TODO: Node 17+ will fail on Windows when running full zlux build, [more information in this thread.](https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported). An upgrade solution needs to be applied across multiple failing components + * **npm** - v6.4 minimum * **jdk** - v8 minimum @@ -79,7 +81,7 @@ On z/OS, git 2.14.4 is the minimum needed. ### Runtime To use the App Server, the following is required: -* **NodeJS** - v14.x (except v14.17.2) up to v16.x is officially supported by the Zowe community. +* **NodeJS** - v16.x up to v18.x is officially supported by the Zowe community. Plugins may depend upon other technologies, such as Java or ZSS. A plugin's [pluginDefinition](https://docs.zowe.org/stable/extend/extend-desktop/mvd-plugindefandstruct) or README will help you to understand if additional prerequisites are needed for that plugin. @@ -143,7 +145,7 @@ When the App Server has started, one of the messages you will see as bootstrappi ### Troubleshooting If you encounter an error message saying `No config file found, initializing`, it means that the App Server could not find a configuration file in the expected location. -To fix this issue, you need to create a zowe.yaml file in the following directory: `%USERPROFILE%\.zowe\workspace\app-server\serverConfig`. You can use [this](https://github.com/zowe/zlux-app-server/blob/v2.x/staging/defaults/serverConfig/zowe.yaml) template as a starting point. +To fix this issue, you need to create a zowe.yaml file in the following directory: `%USERPROFILE%\.zowe\workspace\app-server\serverConfig`. You can use [this](https://github.com/zowe/zlux-app-server/blob/v2.x/staging/defaults/serverConfig/defaults.yaml) template as a starting point. ### Server Logs When the server starts, it writes logs to a text file. On z/OS, Unix, and Linux, the server also logs to the terminal via stdout. diff --git a/bin/configure.sh b/bin/configure.sh index 36276d84..0af66e49 100755 --- a/bin/configure.sh +++ b/bin/configure.sh @@ -28,11 +28,6 @@ if [ "$ZWE_components_gateway_enabled" = "true" ]; then if [ "${ZWE_RUN_ON_ZOS}" != "true" ]; then zss_def_template="zss.apiml_static_reg.yaml.template" export ZSS_PORT="${ZWE_components_zss_port}" - if [ "${ZWE_components_zss_tls}" != "false" ]; then - export ZSS_PROTOCOL=https - else - export ZSS_PROTOCOL=http - fi if [ -n "${ZWE_STATIC_DEFINITIONS_DIR}" ]; then zss_registration_yaml=${ZWE_STATIC_DEFINITIONS_DIR}/zss.apiml_static_reg_yaml_template.${ZWE_CLI_PARAMETER_HA_INSTANCE}.yml @@ -43,7 +38,6 @@ if [ "$ZWE_components_gateway_enabled" = "true" ]; then fi unset ZSS_PORT - unset ZSS_PROTOCOL fi fi fi @@ -52,10 +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_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 d99e7de6..00000000 --- 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 24c0d414..00000000 --- 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/bin/start.sh b/bin/start.sh index c8d5510d..6c24f83d 100755 --- a/bin/start.sh +++ b/bin/start.sh @@ -95,12 +95,6 @@ if [ "$ZWE_components_app_server_dns_lookupOrder" = "ipv6" ]; then ZLUX_DNS_ORDER="--dns-result-order=verbatim" fi -# not all versions of node support the above (14.18+ generally) so we can just try it to see what happens. -v4_check=$(${NODE_BIN} ${ZLUX_DNS_ORDER} -e "console.log('success');") -if [ "${v4_check}" != "success" ]; then - ZLUX_DNS_ORDER= -fi - if [ -z "${ZWED_FLAGS}" ]; then ZWED_FLAGS="${ZLUX_DNS_ORDER} --harmony " fi diff --git a/bin/validate.sh b/bin/validate.sh index d2d542db..32739e81 100644 --- a/bin/validate.sh +++ b/bin/validate.sh @@ -7,7 +7,7 @@ # # Copyright Contributors to the Zowe Project. -type node +result=$(type node) if [ "$?" -ne "0" ]; then if [ -e "${NODE_HOME}/bin/node" ]; then echo "Node found in NODE_HOME" diff --git a/defaults/README.md b/defaults/README.md new file mode 100644 index 00000000..3f7fb833 --- /dev/null +++ b/defaults/README.md @@ -0,0 +1,2 @@ +**Note:** During a full Zowe build, the files in this location become overwritten by files present in: +https://github.com/zowe/zowe-install-packaging/tree/v3.x/staging/files/zlux/config \ No newline at end of file diff --git a/defaults/ZLUX/pluginStorage/org.zowe.zlux.bootstrap/plugins/allowedPlugins.json b/defaults/ZLUX/pluginStorage/org.zowe.zlux.bootstrap/plugins/allowedPlugins.json index 0f61146b..75eaa714 100644 --- a/defaults/ZLUX/pluginStorage/org.zowe.zlux.bootstrap/plugins/allowedPlugins.json +++ b/defaults/ZLUX/pluginStorage/org.zowe.zlux.bootstrap/plugins/allowedPlugins.json @@ -78,6 +78,12 @@ "*" ] }, + { + "identifier": "org.zowe.zlux.ivydesktop", + "versions": [ + "*" + ] + }, { "identifier": "org.zowe.zlux.ng2desktop.settings", "versions": [ diff --git a/defaults/plugins/org.zowe.zlux.ng2desktop.json b/defaults/plugins/org.zowe.zlux.ivydesktop.json similarity index 57% rename from defaults/plugins/org.zowe.zlux.ng2desktop.json rename to defaults/plugins/org.zowe.zlux.ivydesktop.json index 3cf2e2e3..07d62b33 100644 --- a/defaults/plugins/org.zowe.zlux.ng2desktop.json +++ b/defaults/plugins/org.zowe.zlux.ivydesktop.json @@ -1,4 +1,4 @@ { - "identifier": "org.zowe.zlux.ng2desktop", + "identifier": "org.zowe.zlux.ivydesktop", "pluginLocation": "../../zlux-app-manager/virtual-desktop" -} \ No newline at end of file +} diff --git a/defaults/serverConfig/defaults.yaml b/defaults/serverConfig/defaults.yaml index c2daa703..6c114753 100644 --- a/defaults/serverConfig/defaults.yaml +++ b/defaults/serverConfig/defaults.yaml @@ -18,21 +18,42 @@ components: debug: false node: hostname: "${{ function a(){ if (process.env.ZWE_INTERNAL_HOST) { return process.env.ZWE_INTERNAL_HOST; } else if (process.env.ZWE_haInstance_hostname) { return process.env.ZWE_haInstance_hostname; } else { return undefined; } }; a() }}" + http: "${{ function a() { + if (components['app-server'].zowe?.network?.server?.tls?.attls === true) { + return { 'port': components['app-server'].port || Number(7556)}; + } else if (zowe.network?.server?.tls?.attls === true) { + return { 'port': components['app-server'].port || Number(7556)}; + } else { + return undefined; + } }; + a() }}" https: ipAddresses: "${{ function a(){ + let addresses; + if (components['app-server'].zowe?.network?.server?.listenAddresses) { + addresses = components['app-server'].zowe.network.server.listenAddresses; + } else if (zowe.network?.server?.listenAddresses) { + addresses = zowe.network.server.listenAddresses; + } else { + addresses = ['0.0.0.0']; + } if (process.env.ZOWE_LOOPBACK_ADDRESS && process.env.BIND_TO_LOOPBACK == 'true') { - return [ process.env.ZOWE_LOOPBACK_ADDRESS , '0.0.0.0' ]; - } else { return ['0.0.0.0'] } }; + return [ process.env.ZOWE_LOOPBACK_ADDRESS ].concat(addresses); + } else { return addresses } }; a() }}" port: "${{ function a(){ - if (process.env.ZWED_SERVER_HTTPS_PORT) { + if (components['app-server'].zowe?.network?.server?.tls?.attls === true) { + return undefined; + } else if (zowe?.network?.server?.tls?.attls === true) { + return undefined; + } else if (process.env.ZWED_SERVER_HTTPS_PORT) { return Number(process.env.ZWED_SERVER_HTTPS_PORT); } else if (components['app-server'].port) { return components['app-server'].port; } else { return Number(7556); } }; a() }}" keys: '${{ function a() { - if (zowe.certificate?.keystore?.type == "JCERACFKS") { + if (zowe.certificate?.keystore?.type && zowe.certificate.keystore.type.match("JCE.*KS")) { return [ zowe.certificate.keystore.file+"&"+zowe.certificate.keystore.alias ]; } else if (zowe.certificate?.pem?.key) { return [ zowe.certificate.pem.key ]; @@ -40,7 +61,7 @@ components: return [ "../defaults/serverConfig/zlux.keystore.key" ]; } }; a() }}' certificates: '${{ function a(){ - if (zowe.certificate?.keystore?.type == "JCERACFKS") { + if (zowe.certificate?.keystore?.type && zowe.certificate.keystore.type.match("JCE.*KS")) { return [ zowe.certificate.keystore.file+"&"+zowe.certificate.keystore.alias ]; } else if (zowe.certificate?.pem?.certificate) { return [ zowe.certificate.pem.certificate ]; @@ -48,7 +69,7 @@ components: return [ "../defaults/serverConfig/zlux.keystore.cer" ]; } }; a() }}' certificateAuthorities: '${{ function a() { - if (zowe.certificate?.truststore?.type == "JCERACFKS") { + if (zowe.certificate?.truststore?.type && zowe.certificate.truststore.type.match("JCE.*KS")) { return [ zowe.certificate.truststore.file ]; } else if(zowe.certificate?.pem?.certificateAuthorities) { if (Array.isArray(zowe.certificate.pem.certificateAuthorities)) { @@ -58,16 +79,55 @@ components: } } else { return ["../defaults/serverConfig/apiml-localca.cer"]; } }; a() }}' + maxTls: '${{ function a(){ + if (components["app-server"].zowe?.network?.server?.tls?.maxTls) { + return components["app-server"].zowe.network.server.tls.maxTls; + } else if (zowe.network?.server?.tls?.maxTls) { + return zowe.network.server.tls.maxTls; + } else { + return "TLSv1.3"; } }; + a() }}' + minTls: '${{ function a(){ + if (components["app-server"].zowe?.network?.server?.tls?.minTls) { + return components["app-server"].zowe.network.server.tls.minTls; + } else if (zowe.network?.server?.tls?.minTls) { + return zowe.network.server.tls.minTls; + } else { + return "TLSv1.2"; } }; + a() }}' + ciphers: '${{ function a(){ + if (components["app-server"].zowe?.network?.server?.tls?.ciphers) { + return components["app-server"].zowe.network.server.tls.ciphers.join(":"); + } else if (zowe.network?.server?.tls?.ciphers) { + return zowe.network.server.tls.ciphers.join(":"); + } else { + return "" } }; + a() }}' + curves: '${{ function a(){ + if (components["app-server"].zowe?.network?.server?.tls?.curves) { + return components["app-server"].zowe.network.server.tls.curves; + } else if (zowe.network?.server?.tls?.curves) { + return zowe.network.server.tls.curves; + } else { + return [] } }; + a() }}' loopbackAddress: "${{ function a(){ if (process.env.ZOWE_LOOPBACK_ADDRESS) { return process.env.ZOWE_LOOPBACK_ADDRESS; } else { return undefined; } }; a() }}" mediationLayer: server: # strange but true: to contact discovery in a container, instead reach for the gateway hostname. + # DEPRECATED. Use discoveryUrls instead. hostname: '${{ function a() { if (process.env.ZWE_RUN_IN_CONTAINER=="true" && process.env.ZWE_GATEWAY_HOST) { return process.env.ZWE_GATEWAY_HOST; } else { return zowe.externalDomains[0] } }; a() }}' + discoveryUrls: '${{ function a() { + if (process.env.ZWE_DISCOVERY_SERVICES_LIST) { + return process.env.ZWE_DISCOVERY_SERVICES_LIST.split(","); + } else { + return ["https://"+zowe.externalDomains[0]+":"+components.discovery.port+"/eureka/"] } }; + a() }}' # usually, externalDomains is where gateway is. But on containers, this isnt accessible to containers, so # HACK: special var ZWE_GATEWAY_HOST is used instead gatewayHostname: '${{ function a() { @@ -76,6 +136,7 @@ components: } else { return zowe.externalDomains[0] } }; a() }}' + # DEPRECATED. Use discoveryUrls instead. port: ${{ components.discovery.port }} # strange but true: to contact gateway in a container, dont use the external port. gatewayPort: '${{ function a() { diff --git a/lib/initInstance.js b/lib/initInstance.js index 4aa2b1f3..04080d9e 100644 --- a/lib/initInstance.js +++ b/lib/initInstance.js @@ -10,16 +10,16 @@ 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'); const initUtils = require('./initUtils'); //const upgradeInstance = require('./upgradeInstance'); const os = require('os'); -const ncp = require('ncp').ncp; const { execSync } = require('child_process'); -const mkdirp = require('mkdirp'); +initUtils.printFormattedDebug(`Started initInstance.js, platform=${os.platform()}`); const haInstanceId = yamlConfig.getCurrentHaInstanceId(); let config = {}; @@ -44,43 +44,43 @@ const versionLocation = path.join(destination, 'component.json'); config.productDir = path.join(__dirname, '..', 'defaults'); //Begin generate any missing folders -mkdirp.sync(destination, {mode: initUtils.FOLDER_MODE}); +initUtils.mkdirp(destination, initUtils.FOLDER_MODE); if (!config.siteDir) { config.siteDir = path.join(destination, 'site'); } const sitePluginStorage = path.join(config.siteDir, 'ZLUX', 'pluginStorage'); -mkdirp.sync(sitePluginStorage, {mode: initUtils.FOLDER_MODE}); +initUtils.mkdirp(sitePluginStorage, initUtils.FOLDER_MODE); if (!config.instanceDir) { config.instanceDir = destination; } const instancePluginStorage = path.join(config.instanceDir, 'ZLUX', 'pluginStorage'); -mkdirp.sync(instancePluginStorage, {mode: initUtils.FOLDER_MODE}); +initUtils.mkdirp(instancePluginStorage, initUtils.FOLDER_MODE); const recognizersPluginStorage = path.join(config.instanceDir, 'ZLUX/pluginStorage', 'org.zowe.zlux.ng2desktop/recognizers'); -mkdirp.sync(recognizersPluginStorage, {mode: initUtils.FOLDER_MODE}); +initUtils.mkdirp(recognizersPluginStorage, initUtils.FOLDER_MODE); const actionsPluginStorage = path.join(config.instanceDir, 'ZLUX/pluginStorage/org.zowe.zlux.ng2desktop', 'actions'); -mkdirp.sync(actionsPluginStorage, {mode: initUtils.FOLDER_MODE}); +initUtils.mkdirp(actionsPluginStorage, initUtils.FOLDER_MODE); const instanceConfig = path.join(config.instanceDir, 'serverConfig'); //750 specifically, to keep server config secure -mkdirp.sync(instanceConfig, {mode: 0o0750}); +initUtils.mkdirp(instanceConfig, 0o0750); if (!config.groupsDir) { config.groupsDir = path.join(config.instanceDir, 'groups'); } -mkdirp.sync(config.groupsDir, {mode: initUtils.FOLDER_MODE}); +initUtils.mkdirp(config.groupsDir, initUtils.FOLDER_MODE); if (!config.usersDir) { config.usersDir = path.join(config.instanceDir, 'users'); } -mkdirp.sync(config.usersDir, {mode: initUtils.FOLDER_MODE}); +initUtils.mkdirp(config.usersDir, initUtils.FOLDER_MODE); if (!config.pluginsDir) { config.pluginsDir = path.join(destination, 'plugins'); } -mkdirp.sync(config.pluginsDir, {mode: initUtils.FOLDER_MODE}); +initUtils.mkdirp(config.pluginsDir, initUtils.FOLDER_MODE); function generateComponentJson() { let componentJsonContent; @@ -144,19 +144,17 @@ try { if (siteStorage.length == 0 && instanceStorage.length == 0) { console.log("ZWED5012I - Copying default plugin preferences into instance"); if (os.platform() == 'win32') { - ncp(path.join(config.productDir, 'ZLUX', 'pluginStorage'), instancePluginStorage, function(err){ + fs.cp(path.join(config.productDir, 'ZLUX', 'pluginStorage'), instancePluginStorage, {recursive: true, force: true}, function(err){ if (err) { console.warn('ZWED5005W - Warning: error while copying plugin preferences into instance',err); 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); } } @@ -200,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 3bdef778..3fc7f579 100644 --- a/lib/initUtils.js +++ b/lib/initUtils.js @@ -9,13 +9,18 @@ */ const fs = require('fs'); +const os = require('os'); const path = require('path'); -const mkdirp = require('mkdirp'); 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) { @@ -26,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 @@ -78,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'); - mkdirp.sync(defaultConfigDir); + mkdirp(defaultConfigDir); try { fs.writeFileSync(path.join(defaultConfigDir,'_defaultVT.json'), JSON.stringify({host:process.env['ZWED_SSH_HOST'] ? process.env['ZWED_SSH_HOST'] : "", @@ -94,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'); - mkdirp.sync(defaultConfigDir); + 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/lib/zluxArgs.js b/lib/zluxArgs.js index 5aca76c4..138ece4d 100644 --- a/lib/zluxArgs.js +++ b/lib/zluxArgs.js @@ -16,7 +16,6 @@ const argParser = require('zlux-server-framework/utils/argumentParser'); const jsonUtils = require('zlux-server-framework/lib/jsonUtils'); const mergeUtils = require('zlux-server-framework/utils/mergeUtils'); const yamlConfig = require('zlux-server-framework/utils/yamlConfig'); -const mkdirp = require('mkdirp'); const cluster = require('cluster'); const MVD_ARGS = [ diff --git a/package-lock.json b/package-lock.json index 3b0054ad..c57f16ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,14 @@ { "name": "zlux-app-server", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zlux-app-server", "version": "1.0.0", "dependencies": { - "mkdirp": "~0.5.1", + "yaml": "~2.4.5", "zlux-server-framework": "file:../zlux-server-framework" } }, @@ -16,125 +16,59 @@ "version": "0.0.0-zlux.version.replacement", "license": "EPL-2.0", "dependencies": { - "@rocketsoftware/eureka-js-client": "~4.5.3", + "@rocketsoftware/eureka-js-client": "~4.5.7", "@rocketsoftware/express-ws": "^5.0.0", "accept-language-parser": "~1.5.0", - "axios": "~0.22.0", - "bluebird": "~3.5.1", - "body-parser": "~1.18.3", - "cookie-parser": "~1.4.3", + "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.16.3", - "express-session": "~1.15.6", - "express-static-gzip": "~1.1.3", - "glob": "~7.1.3", - "graceful-fs": "~4.1.15", - "ipaddr.js": "~1.8.0", - "js-yaml": "~3.13.1", - "lodash": "~4.17.21", - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "node-forge": "~1.3.0", + "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", - "rimraf": "~2.6.3", - "semver": "~5.5.1", + "semver": "~7.6.0", "swagger-parser": "~10.0.3", "ws": "^6.2.2", - "yaml": "~1.10.2", - "yauzl": "~2.10.0" + "yaml": "~2.4.1", + "yauzl": "~3.1.2" }, "devDependencies": { - "@types/connect": "3.4.32", - "@types/express": "~4.16.1", - "@types/express-serve-static-core": "4.17.7", - "@types/node": "~6.0.0", + "@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": "~8.4.0", - "typescript": "2.7.1" + "mocha": "~10.2.0", + "typescript": "~5.0.0" }, "optionalDependencies": { + "fsevents": "~2.3.2", "keyring_js": "~1.1.0" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, + "node_modules/yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", "bin": { - "mkdirp": "bin/cmd.js" + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" } }, "node_modules/zlux-server-framework": { "resolved": "../zlux-server-framework", "link": true } - }, - "dependencies": { - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "zlux-server-framework": { - "version": "file:../zlux-server-framework", - "requires": { - "@rocketsoftware/eureka-js-client": "~4.5.3", - "@rocketsoftware/express-ws": "^5.0.0", - "@types/connect": "3.4.32", - "@types/express": "~4.16.1", - "@types/express-serve-static-core": "4.17.7", - "@types/node": "~6.0.0", - "@types/qs": "6.9.3", - "accept-language-parser": "~1.5.0", - "axios": "~0.22.0", - "bluebird": "~3.5.1", - "body-parser": "~1.18.3", - "chai": "~4.2.0", - "chai-http": "~4.2.0", - "cookie-parser": "~1.4.3", - "diffie-hellman": "^5.0.3", - "express": "~4.16.3", - "express-session": "~1.15.6", - "express-static-gzip": "~1.1.3", - "glob": "~7.1.3", - "graceful-fs": "~4.1.15", - "ipaddr.js": "~1.8.0", - "js-yaml": "~3.13.1", - "keyring_js": "~1.1.0", - "lodash": "~4.17.21", - "mkdirp": "~0.5.1", - "mocha": "~8.4.0", - "ncp": "~2.0.0", - "node-forge": "~1.3.0", - "normalize-url": "~7.0.0", - "require-from-string": "~2.0.2", - "rimraf": "~2.6.3", - "semver": "~5.5.1", - "swagger-parser": "~10.0.3", - "typescript": "2.7.1", - "ws": "^6.2.2", - "yaml": "~1.10.2", - "yauzl": "~2.10.0" - } - } } } diff --git a/package.json b/package.json index ba40c9e2..e86efb5b 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "test": "echo \"Warning: no test specified\" && exit 0" }, "dependencies": { - "zlux-server-framework": "file:../zlux-server-framework", - "mkdirp": "~0.5.1" + "yaml": "~2.4.5", + "zlux-server-framework": "file:../zlux-server-framework" } } diff --git a/schemas/app-server-config.json b/schemas/app-server-config.json old mode 100644 new mode 100755 index e8155627..fbdbc51f --- a/schemas/app-server-config.json +++ b/schemas/app-server-config.json @@ -6,6 +6,18 @@ "type": "object", "additionalProperties": true, "properties": { + "zowe": { + "type": "object", + "additionalProperties": true, + "description": "Contains customizations from the Zowe global configuration.", + "properties": { + "useConfigmgr": { + "type": "boolean", + "default": true, + "description": "Determines whether configmgr will be used during the startup proceedure, such as to register plugins." + } + } + }, "node": { "type": "object", "description": "Configuration options specific to the app-server and things it depends upon", @@ -48,6 +60,18 @@ "deprecated": true, "description": "Passes through the secureProtocol attribute to TLS calls of nodeJS, as defined within https://nodejs.org/api/tls.html#tlscreatesecurecontextoptions" }, + "maxTls": { + "type": "string", + "enum": ["TLSv1.2", "TLSv1.3"], + "default": "TLSv1.3", + "description": "Maximum TLS version allowed for network connections." + }, + "minTls": { + "type": "string", + "enum": ["TLSv1.2", "TLSv1.3"], + "default": "TLSv1.2", + "description": "Minimum TLS version allowed for network connections, and less than or equal to maxTls." + }, "ciphers": { "oneOf": [ { "$ref": "#/$defs/nodejsDefaultCiphers" }, @@ -55,6 +79,13 @@ { "$ref": "#/$defs/customCiphers" } ] }, + "curves": { + "type": "array", + "description": "A list of curve NIDs or names, for example P-521, P-384, P-256", + "items": { + "type": "string" + } + }, "enableTrace": { "type": "boolean", "default": false @@ -98,6 +129,14 @@ "type": "boolean", "description": "Controls if connections to the API Mediation Layer gateway and/or discovery service should be HTTP or HTTPS" }, + "discoveryUrls": { + "type": "array", + "description": "A list of URLs where discovery servers can be found. URLs must be in the form of 'https://host:port/eureka/'. This will be used instead of the 'hostname' and 'port' values if present.", + "items": { + "type": "string", + "pattern": "https:\/\/.*:[0-9].*\/eureka\/" + } + }, "gatewayHostname": { "type": "string", "format": "hostname", @@ -106,7 +145,8 @@ "hostname": { "type": "string", "format": "hostname", - "description": "The hostname or IP where the Zowe Discovery service is running" + "description": "The hostname or IP where the Zowe Discovery service is running", + "deprecated": true }, "gatewayPort": { "oneOf": [ @@ -120,6 +160,7 @@ { "$ref": "/schemas/v2/server-common#zoweTcpPort" }, { "$ref": "/schemas/v2/server-common#zoweReservedTcpPort" }, ], + "deprecated": true, "description": "The port where the Zowe Discovery service is running" }, "enabled": { @@ -620,7 +661,7 @@ { "$ref": "#/$defs/zoweDefaultCiphers" } ] }, - "description": "Instructs app-server to use the list of ciphers in this string when using TLS. String must be in the form defined here https://nodejs.org/api/tls.html#modifying-the-default-tls-cipher-suite" + "description": "Instructs app-server to use the list of ciphers in this string when using TLS. String is a colon separated list of IANA or openSSL names" }, "headerCustomization": { "type": "object", @@ -660,14 +701,14 @@ }, "safKeyringCertObject": { "type": "string", - "pattern": "^safkeyring:(\/\/)+.*$" + "pattern": "^safkeyring.*:(\/\/)+.*$" }, "pathCertObject": { "type": "string", "not": { "anyOf": [ { "pattern": "^file:\/\/.*$" }, - { "pattern": "^safkeyring:(\/\/)+.*$" } + { "pattern": "^safkeyring.*:(\/\/)+.*$" } ] } }, diff --git a/schemas/plugindefinition-schema.json b/schemas/plugindefinition-schema.json index 50e074c8..151a039a 100644 --- a/schemas/plugindefinition-schema.json +++ b/schemas/plugindefinition-schema.json @@ -228,6 +228,22 @@ "framework": { "type": "string", "enum": [ "angular2", "angular", "react" ] + }, + "entryPoint": { + "type": "object", + "description": "Describes a path, relative to the app's '/web' folder in the browser, where the main file is that will be loaded by the Zowe Desktop. This is assumed to be 'main.js' for Zowe versions older than v3 but allows you to ship multiple entryPoints to target different versions of Zowe", + "additionalProperties": false, + "properties": { + "2.0": { + "const": "main.js", + "type": "string", + "description": "This property is not configurable. The Zowe Desktop in v2 will always search for web/main.js. This entry is just defined to document this fact." + }, + "3.0": { + "type": "string", + "description": "This is a path, relative to the app's '/web' folder in the browser, where the main file that will be loaded by the Zowe Desktop is located. This should not be 'main.js' to avoid conflict with that name used in Zowe v2." + } + } } } }, diff --git a/zss.apiml_static_reg.yaml.template b/zss.apiml_static_reg.yaml.template index b8eedebb..576568f4 100644 --- a/zss.apiml_static_reg.yaml.template +++ b/zss.apiml_static_reg.yaml.template @@ -4,7 +4,7 @@ services: description: 'Zowe System Services is an HTTPS and Websocket server that makes it easy to have secure, powerful web APIs backed by low-level z/OS constructs. It contains services for essential z/OS abilities such as working with files, datasets, and ESMs, but is also extensible by REST and Websocket "Dataservices" which are optionally present in App Framework "Plugins".' catalogUiTileId: zss instanceBaseUrls: - - ${ZSS_PROTOCOL}://${ZWED_agent_host}:${ZSS_PORT}/ + - https://${ZWED_agent_host}:${ZSS_PORT}/ homePageRelativeUrl: routedServices: - gatewayUrl: api/v1 @@ -17,4 +17,4 @@ services: catalogUiTiles: zss: title: Zowe System Services (ZSS) - description: Zowe System Services is an HTTPS and Websocket server that makes it easy to have secure, powerful web APIs backed by low-level z/OS constructs. \ No newline at end of file + description: Zowe System Services is an HTTPS and Websocket server that makes it easy to have secure, powerful web APIs backed by low-level z/OS constructs.