From 4f13cb373084f611818dd114d304da704b6df196 Mon Sep 17 00:00:00 2001 From: 1000TurquoisePogs Date: Fri, 14 Jun 2024 12:40:02 +0200 Subject: [PATCH 1/3] Validate component appfw plugins during install time Signed-off-by: 1000TurquoisePogs --- CHANGELOG.md | 1 + bin/commands/components/install/extract/index.ts | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a08b716fe0..6485176d2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to the Zowe Installer will be documented in this file. ## `2.17.0` ## New features and enhancements +- Enhancement: `zwe components install` and `zwe components upgrade` now checks the schema validity of any application framework plugins present within a component, so that you will know better if a component is valid prior to running Zowe. [#3866](https://github.com/zowe/zowe-install-packaging/pull/3866) - Enhancement: Added zowe.network.server.tls.attls and zowe.network.client.tls.attls as booleans for controlling global or per-component way to tell Zowe servers that they should operate in a mode compatible with an AT-TLS setup. [#3463](https://github.com/zowe/zowe-install-packaging/pull/3463) ## `2.16.0` diff --git a/bin/commands/components/install/extract/index.ts b/bin/commands/components/install/extract/index.ts index 576a3f3c12..b5e801d4d6 100644 --- a/bin/commands/components/install/extract/index.ts +++ b/bin/commands/components/install/extract/index.ts @@ -150,6 +150,14 @@ export function execute(componentFile: string, autoEncoding?: string, upgrade?: common.printErrorAndExit(`Error ZWEL0167E: Cannot find component name from ${componentFile} package manifest`, undefined, 167); } common.printDebug(`- Component name found as ${componentName}`); + + // If the component has appfw plugins, their validity should be checked against appfw plugin schema. + // If invalid, the installation will exit with an error message. + if (manifest.appfwPlugins) { + manifest.appfwPlugins.forEach((appfwPlugin: {path: string})=> { + component.getPluginDefinition(pathoid.resolve(tmpDir, appfwPlugin.path)); + }); + } const destinationDir = pathoid.resolve(targetDir, componentName); const bkpDir = pathoid.resolve(targetDir, `${componentName}_zwebkp`); From 93b9fd83191bb6b771b9ae6d5a731a9801f0b83d Mon Sep 17 00:00:00 2001 From: 1000TurquoisePogs Date: Fri, 14 Jun 2024 12:49:09 +0200 Subject: [PATCH 2/3] Cleanup on schema failure by not exiting in getPluginDefinition so that exit can be handled after cleanup Signed-off-by: 1000TurquoisePogs --- .../components/install/extract/index.ts | 8 +++++++- bin/libs/component.ts | 20 +++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/bin/commands/components/install/extract/index.ts b/bin/commands/components/install/extract/index.ts index b5e801d4d6..9a7685904d 100644 --- a/bin/commands/components/install/extract/index.ts +++ b/bin/commands/components/install/extract/index.ts @@ -155,7 +155,13 @@ export function execute(componentFile: string, autoEncoding?: string, upgrade?: // If invalid, the installation will exit with an error message. if (manifest.appfwPlugins) { manifest.appfwPlugins.forEach((appfwPlugin: {path: string})=> { - component.getPluginDefinition(pathoid.resolve(tmpDir, appfwPlugin.path)); + let result = component.getPluginDefinition(pathoid.resolve(tmpDir, appfwPlugin.path), true); + //Normally, getPluginDefinition would quit upon failure. But we want to cleanup the tmpDir before that. + // So, we pass true to allow it to continue, check for null, and then remove the tmpdir and exit if so. + if (result === null) { + fs.rmrf(tmpDir); + std.exit(1); + } }); } diff --git a/bin/libs/component.ts b/bin/libs/component.ts index ddf6eb3037..2f8611db9c 100644 --- a/bin/libs/component.ts +++ b/bin/libs/component.ts @@ -114,28 +114,30 @@ function showExceptions(e: any,depth: number): void { } } -export function getPluginDefinition(pluginRootPath:string) { +export function getPluginDefinition(pluginRootPath:string, continueOnFailure?: boolean) { const pluginDefinitionPath = `${pluginRootPath}/pluginDefinition.json`; + const printer = continueOnFailure ? common.printError : common.printErrorAndExit; + if (fs.fileExists(pluginDefinitionPath)) { let status; if ((status = CONFIG_MGR.addConfig(pluginRootPath))) { - common.printErrorAndExit(`Could not add config for ${pluginRootPath}, status=${status}`); + printer(`Could not add config for ${pluginRootPath}, status=${status}`); return null; } if ((status = CONFIG_MGR.loadSchemas(pluginRootPath, PLUGIN_DEF_SCHEMAS))) { - common.printErrorAndExit(`Could not load schemas ${PLUGIN_DEF_SCHEMAS} for plugin ${pluginRootPath}, status=${status}`); + printer(`Could not load schemas ${PLUGIN_DEF_SCHEMAS} for plugin ${pluginRootPath}, status=${status}`); return null; } if ((status = CONFIG_MGR.setConfigPath(pluginRootPath, `FILE(${pluginDefinitionPath})`))) { - common.printErrorAndExit(`Could not set config path for ${pluginDefinitionPath}, status=${status}`); + printer(`Could not set config path for ${pluginDefinitionPath}, status=${status}`); return null; } if ((status = CONFIG_MGR.loadConfiguration(pluginRootPath))) { - common.printErrorAndExit(`Could not load config for ${pluginDefinitionPath}, status=${status}`); + printer(`Could not load config for ${pluginDefinitionPath}, status=${status}`); return null; } @@ -144,17 +146,19 @@ export function getPluginDefinition(pluginRootPath:string) { if (validation.exceptionTree){ common.printError(`Validation of ${pluginDefinitionPath} against schema ${PLUGIN_DEF_SCHEMA_ID} found invalid JSON Schema data`); showExceptions(validation.exceptionTree, 0); - std.exit(1); + if (!continueOnFailure) { + std.exit(1); + } return null; } else { return CONFIG_MGR.getConfigData(pluginRootPath); } } else { - common.printErrorAndExit(`Error occurred on validation of ${pluginDefinitionPath} against schema ${PLUGIN_DEF_SCHEMA_ID} `); + printer(`Error occurred on validation of ${pluginDefinitionPath} against schema ${PLUGIN_DEF_SCHEMA_ID} `); return null; } } else { - common.printErrorAndExit(`Plugin at ${pluginRootPath} has no pluginDefinition.json`); + printer(`Plugin at ${pluginRootPath} has no pluginDefinition.json`); return null; } } From 02fd64cacd277df691c9af1f461c546af5371f85 Mon Sep 17 00:00:00 2001 From: 1000TurquoisePogs Date: Mon, 24 Jun 2024 16:23:34 +0200 Subject: [PATCH 3/3] Fix that manifest and plugindef were using same configmgr config id Signed-off-by: 1000TurquoisePogs --- bin/libs/component.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bin/libs/component.ts b/bin/libs/component.ts index 2f8611db9c..457a12364c 100644 --- a/bin/libs/component.ts +++ b/bin/libs/component.ts @@ -116,32 +116,33 @@ function showExceptions(e: any,depth: number): void { export function getPluginDefinition(pluginRootPath:string, continueOnFailure?: boolean) { const pluginDefinitionPath = `${pluginRootPath}/pluginDefinition.json`; + const configId = `appfwPlugin:${pluginRootPath}`; const printer = continueOnFailure ? common.printError : common.printErrorAndExit; if (fs.fileExists(pluginDefinitionPath)) { let status; - if ((status = CONFIG_MGR.addConfig(pluginRootPath))) { + if ((status = CONFIG_MGR.addConfig(configId))) { printer(`Could not add config for ${pluginRootPath}, status=${status}`); return null; } - if ((status = CONFIG_MGR.loadSchemas(pluginRootPath, PLUGIN_DEF_SCHEMAS))) { + if ((status = CONFIG_MGR.loadSchemas(configId, PLUGIN_DEF_SCHEMAS))) { printer(`Could not load schemas ${PLUGIN_DEF_SCHEMAS} for plugin ${pluginRootPath}, status=${status}`); return null; } - if ((status = CONFIG_MGR.setConfigPath(pluginRootPath, `FILE(${pluginDefinitionPath})`))) { + if ((status = CONFIG_MGR.setConfigPath(configId, `FILE(${pluginDefinitionPath})`))) { printer(`Could not set config path for ${pluginDefinitionPath}, status=${status}`); return null; } - if ((status = CONFIG_MGR.loadConfiguration(pluginRootPath))) { + if ((status = CONFIG_MGR.loadConfiguration(configId))) { printer(`Could not load config for ${pluginDefinitionPath}, status=${status}`); return null; } - let validation = CONFIG_MGR.validate(pluginRootPath); + let validation = CONFIG_MGR.validate(configId); if (validation.ok){ if (validation.exceptionTree){ common.printError(`Validation of ${pluginDefinitionPath} against schema ${PLUGIN_DEF_SCHEMA_ID} found invalid JSON Schema data`); @@ -151,7 +152,7 @@ export function getPluginDefinition(pluginRootPath:string, continueOnFailure?: b } return null; } else { - return CONFIG_MGR.getConfigData(pluginRootPath); + return CONFIG_MGR.getConfigData(configId); } } else { printer(`Error occurred on validation of ${pluginDefinitionPath} against schema ${PLUGIN_DEF_SCHEMA_ID} `);