diff --git a/CHANGELOG.md b/CHANGELOG.md index f2bb8b7dc4..c4b875a694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ All notable changes to the Zowe Installer will be documented in this file. ## `3.2.0` +- Enhancement: `zwe support` command collecting more environment details [#4147](https://github.com/zowe/zowe-install-packaging/pull/4147) - Enhancement: Added new library function formatZosVersion() [#4134](https://github.com/zowe/zowe-install-packaging/pull/4134) +- `zwe support verify-fingerprints` no longer requires a `zowe.yaml` to be passed as a command-line parameter. ## `3.1.0` - Bugfix: When logging `zwe` command, sometimes the log has wrong file tag and the log is unreadable. [#4071](https://github.com/zowe/zowe-install-packaging/pull/4071) diff --git a/bin/commands/internal/start/prepare/index.ts b/bin/commands/internal/start/prepare/index.ts index 08feceb953..1c944c1243 100644 --- a/bin/commands/internal/start/prepare/index.ts +++ b/bin/commands/internal/start/prepare/index.ts @@ -24,6 +24,7 @@ import * as config from '../../../../libs/config'; import * as component from '../../../../libs/component'; import * as varlib from '../../../../libs/var'; import * as java from '../../../../libs/java'; +import * as javaCI from '../../../../libs/java_ci'; import * as node from '../../../../libs/node'; import * as zosmf from '../../../../libs/zosmf'; @@ -148,7 +149,7 @@ function globalValidate(enabledComponents:string[]): void { // validate java for some core components //TODO this should be a manifest parameter that you require java, not a hardcoded list. What if extensions require it? if (enabledComponents.includes('gateway') || enabledComponents.includes('zaas') || enabledComponents.includes('discovery') || enabledComponents.includes('api-catalog') || enabledComponents.includes('caching-service')) { - let javaOk = java.validateJavaHome(); + let javaOk = javaCI.validateJavaHome(); if (!javaOk) { privateErrors++; common.printFormattedError('ZWELS', "zwe-internal-start-prepare,global_validate", `Could not validate java home`); diff --git a/bin/commands/support/.errors b/bin/commands/support/.errors new file mode 100644 index 0000000000..2e6e4943c1 --- /dev/null +++ b/bin/commands/support/.errors @@ -0,0 +1,2 @@ +ZWEL0150E|150|Failed to find file %s. Zowe runtimeDirectory is invalid. +ZWEL0151E|151|Failed to create temporary file %s. Please check permission or volume free space. diff --git a/bin/commands/support/.examples b/bin/commands/support/.examples new file mode 100644 index 0000000000..9cf4425974 --- /dev/null +++ b/bin/commands/support/.examples @@ -0,0 +1,5 @@ +zwe support -c /path/to/zowe.yaml + +zwe support -c /path/to/zowe.yaml --target-dir /path/to/save/support/results + +zwe support --config 'FILE(/path/to/zowe.yaml):PARMLIB(ZOWE.PARMLIB(ZWEYAML))' diff --git a/bin/commands/support/.help b/bin/commands/support/.help index 6bb8eb4639..958c33f110 100644 --- a/bin/commands/support/.help +++ b/bin/commands/support/.help @@ -5,9 +5,13 @@ This command will collect these information: - Environment * z/OS version * Java version + * Java keytool TLS information * Node.js version + * zOSMF status * External Security Manager * CEE Runtime Options + * Filesystem flags + * ZSS Program Controlled extended attribute - Zowe configurations * Zowe manifest.json * Zowe configuration file @@ -17,5 +21,4 @@ This command will collect these information: * Zowe APIML static registration files under "`zowe.workspaceDirectory`/api-mediation/api-defs" - Zowe runtime * Active running Zowe processes - * Zowe job log - Zowe fingerprints and validation result diff --git a/bin/commands/support/cli.js b/bin/commands/support/cli.js new file mode 100644 index 0000000000..f4c250939a --- /dev/null +++ b/bin/commands/support/cli.js @@ -0,0 +1,14 @@ +/* + 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 index from './index'; + +index.execute(); diff --git a/bin/commands/support/index.sh b/bin/commands/support/index.sh index 20c024ef43..23defb60de 100644 --- a/bin/commands/support/index.sh +++ b/bin/commands/support/index.sh @@ -11,136 +11,4 @@ # Copyright Contributors to the Zowe Project. ####################################################################### -print_message_file() { - msg="${1}" - out_file="${2}" - print_message "- ${msg}" - echo "${msg}" >> "${out_file}" -} - -print_level0_message "Collect information for Zowe support" - -############################### -# constants -DATE=`date +%Y-%m-%d-%H-%M-%S` -target_dir="${ZWE_CLI_PARAMETER_TARGET_DIR}" -if [ -z "${target_dir}" ]; then - target_dir=$(get_tmp_dir) -else - curr_pwd=$(pwd) - cd "${target_dir}" - target_dir=$(pwd) - cd "${curr_pwd}" -fi -tmp_file_prefix=zwe-support -tmp_pax="${target_dir}/${tmp_file_prefix}.${DATE}.pax" -tmp_dir=$(create_tmp_file "${tmp_file_prefix}" "${target_dir}") - -############################### -# validate -require_java -require_node -require_zowe_yaml - -############################### -print_message "Started at ${DATE}" -mkdir "${tmp_dir}" -chmod 700 "${tmp_dir}" -print_debug "Temporary directory created: ${tmp_dir}" -print_message - -############################### -print_level1_message "Collecting various environment information" -ENVIRONMENT_FILE="${tmp_dir}/environment_output" -echo "[Environment information]" > "${ENVIRONMENT_FILE}" -ZOS_VERSION=`operator_command "D IPLINFO" | grep -i release | xargs` -if [ -z "${ZOS_VERSION}" ]; then - ZOS_VERSION=`sysvar SYSOSLVL` -fi -JAVA_VERSION=`${JAVA_HOME}/bin/java -version 2>&1 | head -n 1` -NODE_VERSION=`${NODE_HOME}/bin/node --version` -ESM=`"${ZWE_zowe_runtimeDirectory}/bin/utils/getesm"` -CEE_OPTIONS=`tsocmd "OMVS RUNOPTS('RPTOPTS(ON)')" 2>&1` - -print_message_file "z/OS version: ${ZOS_VERSION}" "${ENVIRONMENT_FILE}" -print_message_file "Java version: ${JAVA_VERSION}" "${ENVIRONMENT_FILE}" -print_message_file "NodeJS version: ${NODE_VERSION}" "${ENVIRONMENT_FILE}" -print_message_file "External Security Manager: ${ESM}" "${ENVIRONMENT_FILE}" -print_message_file "CEE Runtime Options: ${CEE_OPTIONS}" "${ENVIRONMENT_FILE}" -print_message - -############################### -print_level1_message "Collecting Zowe configurations" -print_message "- manifest.json" -cp "${ZWE_zowe_runtimeDirectory}/manifest.json" "${tmp_dir}" -print_message "- zowe.yaml" -cp "${ZWE_CLI_PARAMETER_CONFIG}" "${tmp_dir}" -ZWE_zowe_workspaceDirectory=$(read_yaml "${ZWE_CLI_PARAMETER_CONFIG}" ".zowe.workspaceDirectory") -if [ -d "${ZWE_zowe_workspaceDirectory}/.env" ]; then - print_message "- /.env" - mkdir -p "${tmp_dir}/workspace" - cp -r "${ZWE_zowe_workspaceDirectory}/.env" "${tmp_dir}/workspace" -fi -if [ -d "${ZWE_zowe_workspaceDirectory}/api-mediation/api-defs" ]; then - print_message "- /api-mediation/api-defs" - mkdir -p "${tmp_dir}/workspace" - cp -r "${ZWE_zowe_workspaceDirectory}/api-mediation/api-defs" "${tmp_dir}/workspace" -fi -ZWE_zowe_setup_certificate_pkcs12_directory=$(read_yaml "${ZWE_CLI_PARAMETER_CONFIG}" ".zowe.setup.certificate.pkcs12.directory") -if [ -d "${ZWE_zowe_setup_certificate_pkcs12_directory}" ]; then - print_message "- ${ZWE_zowe_setup_certificate_pkcs12_directory}" - mkdir -p "${tmp_dir}/keystore" - cp -r "${ZWE_zowe_setup_certificate_pkcs12_directory}" "${tmp_dir}/keystore" -fi -print_message - -############################### -print_level1_message "Collecting Zowe file fingerprints" -print_message "- copy original fingerprints" -cp -r "${ZWE_zowe_runtimeDirectory}/fingerprint" "${tmp_dir}" -print_message "- verify fingerprints" -result=$(export ZWE_PRIVATE_LOG_FILE="${tmp_dir}/verify-fingerprints.log" && export ZWE_PRIVATE_LOG_LEVEL_ZWELS=TRACE && touch "${ZWE_PRIVATE_LOG_FILE}" && . "${ZWE_zowe_runtimeDirectory}/bin/commands/support/verify-fingerprints/index.sh" 2>/dev/null 1>/dev/null) -print_message - -############################### -job_name=$(read_yaml "${ZWE_CLI_PARAMETER_CONFIG}" ".zowe.job.name") -job_prefix=$(read_yaml "${ZWE_CLI_PARAMETER_CONFIG}" ".zowe.job.prefix") -print_level1_message "Collecting current process information based on the job prefix ${job_prefix} and job name ${job_name}" -PS_OUTPUT_FILE=${tmp_dir}"/ps_output" - -# Collect process information -print_message "- Adding ${PS_OUTPUT_FILE}" -ps -A -o pid,ppid,time,etime,user,jobname,args | grep -e "^[[:space:]]*PID" -e "${job_prefix}" -e "${job_name}" > $PS_OUTPUT_FILE -print_message - -############################### -# TODO: job log -# To avoid of using SDSF, we used to use TSO output command to export job log -# but it always fails with below error for me: -# IKJ56328I JOB ZWE1SV REJECTED - JOBNAME MUST BE YOUR USERID OR MUST START WITH YOUR USERID -# REF: https://www.ibm.com/docs/en/zos/2.3.0?topic=subcommands-output-command -# REF: https://www.ibm.com/docs/en/zos/2.3.0?topic=ikj-ikj56328i - -############################### -# Collect instance logs -log_dir=$(read_yaml "${ZWE_CLI_PARAMETER_CONFIG}" ".zowe.logDirectory") -print_level1_message "Collecting logs from ${log_dir}" -if [ -d "${log_dir}" ]; then - cp -r "${log_dir}" "${tmp_dir}" -fi -print_message - -############################### -print_level1_message "Create support package and clean up" -curr_pwd=$(pwd) -cd "${tmp_dir}" -pax -w -v -o saveext -f "${tmp_pax}" . -compress "${tmp_pax}" -chmod 700 "${tmp_pax}"* -cd "${curr_pwd}" -rm -fr "${tmp_dir}" -print_message - -############################### -# exit message -print_level1_message "Zowe support package is generated as ${tmp_pax}.Z" +_CEE_RUNOPTS="XPLINK(ON)" ${ZWE_zowe_runtimeDirectory}/bin/utils/configmgr -script "${ZWE_zowe_runtimeDirectory}/bin/commands/support/cli.js" diff --git a/bin/commands/support/index.ts b/bin/commands/support/index.ts new file mode 100644 index 0000000000..96cddb877f --- /dev/null +++ b/bin/commands/support/index.ts @@ -0,0 +1,238 @@ +/* + 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. +*/ + +// TODO: job log +// To avoid of using SDSF, we used to use TSO output command to export job log +// but it always fails with below error for me: +// IKJ56328I JOB ZWE1SV REJECTED - JOBNAME MUST BE YOUR USERID OR MUST START WITH YOUR USERID +// REF: https://www.ibm.com/docs/en/zos/2.3.0?topic=subcommands-output-command +// REF: https://www.ibm.com/docs/en/zos/2.3.0?topic=ikj-ikj56328i + +import * as std from 'cm_std'; +import * as zos from 'zos'; +import * as xplatform from 'xplatform' + +import * as common from '../../libs/common'; +import * as component from '../../libs/component'; +import * as config from '../../libs/config'; +import * as fs from '../../libs/fs'; +import * as java from '../../libs/java'; +import * as javaCI from '../../libs/java_ci'; +import * as node from '../../libs/node' +import * as shell from '../../libs/shell'; +import * as zoslib from '../../libs/zos'; +import * as zosfs from '../../libs/zos-fs'; +import * as zosmf from '../../libs/zosmf'; + +import * as verifyFingerprints from './verify-fingerprints/index'; + +function zssCheck(zssBinary: string): string { + if (fs.fileExists(zssBinary)) { + return `${zssBinary} = ${component.hasPCBit(zssBinary)}` + } else { + common.printError(`Error ZWEL0150E: Failed to find file "${zssBinary}". Zowe runtimeDirectory is invalid.`); + return `Missing file: ${zssBinary}` + } +} + +export function execute(): void { + + common.printLevel0Message('Collect information for Zowe support'); + const isoDate = new Date().toISOString().replace(/T|t|:/g,'-').substring(0,19); + + let targetDirectory = std.getenv('ZWE_CLI_PARAMETER_TARGET_DIR'); + if (targetDirectory) { + targetDirectory = fs.convertToAbsolutePath(targetDirectory); + } else { + targetDirectory = fs.getTmpDir(); + } + + if (!fs.directoryExists(targetDirectory, true)) { + common.printErrorAndExit(`Error ZWELxxxx: "${targetDirectory}" is not a valid directory.`, undefined, 999); + } + + const tmpFilePrefix = 'zwe-support'; + const tmpPax = `${targetDirectory}/${tmpFilePrefix}.${isoDate}.pax`; + const tmpDir = fs.createTmpFile(tmpFilePrefix, targetDirectory); + + common.requireZoweYaml(); + const ZOWE_CONFIG=config.getZoweConfig(); + + common.printMessage(`Started at ${isoDate}`); + fs.mkdirp(tmpDir, 0o700); + common.printDebug(`Temporary directory created: ${tmpDir}`); + + common.printLevel1Message('Collecting various environment information'); + const environmentFile =`${tmpDir}/environment.json`; + let environment = {}; + + environment["zos-version"] = zoslib.formatZosVersion(); + + const enabledComponents = component.getEnabledComponents(); + if (enabledComponents.includes('app-server')) { + node.requireNode(); + } + const nodeHome = std.getenv('NODE_HOME'); + if (nodeHome) { + const nodeVersion = shell.execOutSync('sh', '-c', '${NODE_HOME}/bin/node -v 2>&1 | head -n 1'); + if (nodeVersion.rc == 0 && nodeVersion.out) { + environment["node"] = `${nodeVersion.out}`; + const discovery = ZOWE_CONFIG.components?.discovery?.enabled; + const zosmfHost = ZOWE_CONFIG.zOSMF?.host; + const zosmfPort = ZOWE_CONFIG.zOSMF?.port; + if (discovery && zosmfHost && zosmfPort) { + environment["zosmf_check"] = `'https://${zosmfHost}:${zosmfPort}/zosmf/info' => ${zosmf.validateZosmfHostAndPort(zosmfHost, zosmfPort)}`; + } + } + } else { + environment["node"] = `not found`; + } + + java.requireJava(); + javaCI.validateJavaHome(); + const javaVersion = shell.execOutSync('sh', '-c', '${JAVA_HOME}/bin/java -version 2>&1 | head -n 1'); + if (javaVersion.rc == 0 && javaVersion.out) { + environment["java"] = javaVersion.out.replace(/\"/g, ''); + const keytoolInfo = shell.execOutSync('sh', '-c', '${JAVA_HOME}/bin/keytool -showinfo -tls 2>&1'); + if (keytoolInfo.rc == 0 && keytoolInfo.out) { + environment["keytool_showinfo_tls"] = keytoolInfo.out.split('\n'); + } + } + + environment["esm"] = `${zos.getEsm()}`; + + // This command is usually failing with rc=12 and FSUM2051I/FSUM2052I messages: + // FSUM2051I The OMVS command failed because the display screen size is not supported.+ + // FSUM2052I The screen size must be at least 12 by 40 but less than 10000 bytes total. The actual primary screen size is 255 by 255 ( + // 65025 bytes). The alternate screen size is 255 by 255 (65025 bytes). + // - tsoCommand will fail and print error -> we will use shell.execOutSync + // - We will take stdout + stderr and try to filter out messages + const ceeOptions = shell.execOutSync('sh', '-c', `tsocmd "OMVS RUNOPTS('RPTOPTS(ON)')" 2>&1`); + if (ceeOptions.out) { + const ceeOptionesLines = ceeOptions.out.split('\n'); + environment["cee_runtime"] = []; + for (let line in ceeOptionesLines) { + if (!(/^FSUM205[1|2]I/).test(ceeOptionesLines[line]) && !(/.*the alternate screen size is.*/i).test(ceeOptionesLines[line])) { + if (ceeOptionesLines[line].trim()) { + environment["cee_runtime"].push(ceeOptionesLines[line]); + } + } + } + } + + const zoweRuntime = std.getenv('ZWE_zowe_runtimeDirectory'); + const fsFlags = zosfs.getFileSystemFlags(zoweRuntime); + if (fsFlags.rc == 0) { + environment["fs_flags"] = (({ rc, ...others }) => others)(fsFlags); // Do not include "rc" + } + + const zssEnabled = ZOWE_CONFIG.components?.zss?.enabled; + if (zssEnabled) { + const zssBinary = `${zoweRuntime}/components/zss/bin/zssServer`; + environment["zss_program_controlled"] = { + "31bit": `${zssCheck(zssBinary)}`, + "64bit": `${zssCheck(`${zssBinary}64`)}` + } + } + + common.printMessage(JSON.stringify(environment, null, 2)); + const saveEnvRc = xplatform.storeFileUTF8(environmentFile, xplatform.AUTO_DETECT, JSON.stringify(environment, null, 2)); + if (saveEnvRc) { + common.printErrorAndExit(`Error ZWEL0151E: Failed to create temporary file "${environmentFile}". Please check permission or volume free space.`, undefined, 151); + } + + common.printLevel1Message('Collecting Zowe configurations'); + common.printMessage(`- manifest.json: ${zoweRuntime}/manifest.json`); + fs.cp(`${zoweRuntime}/manifest.json`, tmpDir); + + const workspaceDirectory = ZOWE_CONFIG.zowe.workspaceDirectory; + + common.printMessage(`- configuration: ${workspaceDirectory}/.env/.zowe-merged.yaml`); + + // workspace directory must exists, otherwise the merging of configs already failed + fs.cp(`${workspaceDirectory}/.env/.zowe-merged.yaml`, `${tmpDir}/zowe-merged.yaml`); + + common.printMessage(`- zowe.workspaceDirectory: ${workspaceDirectory}/.env`); + fs.mkdirp(`${tmpDir}/workspace`); + fs.cp(`${workspaceDirectory}/.env`, `${tmpDir}/workspace`) + + if (fs.directoryExists(`${workspaceDirectory}/api-mediation/api-defs`)) { + common.printMessage(`- zowe.workspaceDirectory: ${workspaceDirectory}/api-mediation/api-defs`); + fs.cp(`${workspaceDirectory}/api-mediation/api-defs`, `${tmpDir}/workspace/api-mediation`); + } + + const pkcs12Directory = ZOWE_CONFIG.zowe.setup?.certificate?.pkcs12?.directory; + if (fs.directoryExists(pkcs12Directory)) { + common.printMessage(`- zowe.setup.certificate.pkcs12.directory: ${pkcs12Directory}`) + fs.mkdirp(`${tmpDir}/keystore`); + fs.cp(`${pkcs12Directory}`, `${tmpDir}/keystore`); + } + common.printMessage(""); + + common.printLevel1Message("Collecting Zowe file fingerprints"); + const verifyFingerprintsFile = `${tmpDir}/verify-fingerprints.log`; + const supportLogFile = std.getenv('ZWE_PRIVATE_LOG_FILE'); + const supportLogLevel = std.getenv('ZWE_PRIVATE_LOG_LEVEL_ZWELS'); + common.printMessage(`- copy original fingerprints: ${zoweRuntime}/fingerprint`); + fs.cp(`${zoweRuntime}/fingerprint`, tmpDir); + common.printMessage("- verify fingerprints"); + std.setenv('ZWE_PRIVATE_LOG_FILE', `${verifyFingerprintsFile}`); + std.setenv('ZWE_PRIVATE_LOG_LEVEL_ZWELS', 'TRACE'); + fs.createFile(verifyFingerprintsFile, 0o700); + verifyFingerprints.execute(true, std.getenv('JAVA_HOME')); + std.setenv('ZWE_PRIVATE_LOG_FILE',`${supportLogFile}`); + std.setenv('ZWE_PRIVATE_LOG_LEVEL_ZWELS', `${supportLogLevel}`); + common.printMessage(""); + + // zowe.job.name + prefix are in defaults + const jobName = ZOWE_CONFIG.zowe.job.name; + const jobPrefix = ZOWE_CONFIG.zowe.job.prefix; + + common.printLevel1Message(`Collecting current process information based on the job prefix ${jobPrefix} and job name ${jobName}`); + + const psOutputFile = `${tmpDir}/ps_output`; + const grepCommand = `grep -i -e "^[[:space:]]*PID" -e "${jobPrefix}" -e "${jobName}"`; + const processes = shell.execOutSync('sh', '-c', `ps -A -o pid,ppid,time,etime,user,jobname,args | ${grepCommand}`); + // This process has the argument, which (surprise!) is found by grep, e.g: + // 1234 5678 00:00:00 00:00:00 ADMDIN USERID grep -i -e ^[[:space:]]*PID -e ZWE1 -e ZWE2 + if (processes.rc == 0 && processes.out) { + let processesSplit = processes.out.split("\n"); + for (let i = 0; i < processesSplit.length; i++) { + if (processesSplit[i].includes(grepCommand.replace(/\"/g, ''))) { + processesSplit.splice(i, 1); + } + } + if (processesSplit.length > 1) { + common.printMessage(`- Adding ${psOutputFile}`); + xplatform.storeFileUTF8(psOutputFile, xplatform.AUTO_DETECT, processesSplit.join("\n")); + } + } + common.printMessage(""); + + const logDirectory=ZOWE_CONFIG.zowe.logDirectory; // This could be /dev/null + if (fs.directoryExists(logDirectory, true)) { + common.printLevel1Message(`Collecting logs from ${logDirectory}`); + fs.mkdirp(`${tmpDir}/logs`); + fs.cp(`${logDirectory}`, `${tmpDir}/logs`); + } + common.printMessage(""); + + common.printLevel1Message('Create support package and clean up'); + shell.execOutSync('sh', '-c', `cd "${tmpDir}" && pax -w -v -o saveext -f "${tmpPax}" . && compress ${tmpPax} && chmod 700 "${tmpPax}.Z"`); + fs.rmrf(tmpDir); + common.printMessage(""); + + if (fs.fileExists(`${tmpPax}.Z`)) { + common.printLevel1Message(`Zowe support package is generated as ${tmpPax}.Z`); + } else { + common.printErrorAndExit(`Error ZWEL0151E: Failed to create file "${tmpPax}.Z". Please check permission or volume free space.`, undefined, 151); + } +} diff --git a/bin/commands/support/verify-fingerprints/.errors b/bin/commands/support/verify-fingerprints/.errors index c3d8948d48..94f583e48c 100644 --- a/bin/commands/support/verify-fingerprints/.errors +++ b/bin/commands/support/verify-fingerprints/.errors @@ -1,4 +1,5 @@ ZWEL0113E|113|Failed to find Zowe version. Please validate your Zowe directory. +ZWEL0122E|122|Cannot find java. Please define JAVA_HOME environment variable. ZWEL0150E|150|Failed to find file %s. Zowe runtimeDirectory is invalid. ZWEL0151E|151|Failed to create temporary file %s. Please check permission or volume free space. ZWEL0181E|181|Failed to verify Zowe file fingerprints. diff --git a/bin/commands/support/verify-fingerprints/.help b/bin/commands/support/verify-fingerprints/.help index 70631b94cd..39534656e2 100644 --- a/bin/commands/support/verify-fingerprints/.help +++ b/bin/commands/support/verify-fingerprints/.help @@ -1,13 +1,11 @@ This command will gather the hash (fingerprint) of every file in the `zowe.runtimeDirectory`. The result is then compared with existing hashes. -For best results, it is recommended to set all directories in the `zowe.yaml` configuration file -which reside outside the `zowe.runtimeDirectory`. These are typically `zowe.workspaceDirectory`, -`zowe.logDirectory` and certificates directories. - -Java is required to run the hash utility. Make sure one of the following is set: -* Environment variable `JAVA_HOME` -* Environment variable `PATH` which includes `java`. +Java is required to run the hash utility. Make sure the environment variable `JAVA_HOME` +is set to the directory containing `bin/java`. -Note: The `JAVA_HOME` path is the directory containing `bin/java`. For example, if java is at '/usr/lpp/java/current/bin/java', then set `JAVA_HOME` to '/usr/lpp/java/current'. + +NOTE: For best results, it is recommended to set all directories in the `zowe.yaml` configuration file +which reside outside the `zowe.runtimeDirectory`. These are typically `zowe.workspaceDirectory`, +`zowe.logDirectory` and certificates directories. diff --git a/bin/commands/support/verify-fingerprints/cli.js b/bin/commands/support/verify-fingerprints/cli.js new file mode 100644 index 0000000000..f4c250939a --- /dev/null +++ b/bin/commands/support/verify-fingerprints/cli.js @@ -0,0 +1,14 @@ +/* + 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 index from './index'; + +index.execute(); diff --git a/bin/commands/support/verify-fingerprints/index.sh b/bin/commands/support/verify-fingerprints/index.sh index 3a67cc129b..e49728dd07 100644 --- a/bin/commands/support/verify-fingerprints/index.sh +++ b/bin/commands/support/verify-fingerprints/index.sh @@ -11,121 +11,4 @@ # Copyright Contributors to the Zowe Project. ####################################################################### -print_level0_message "Verify Zowe file fingerprints" - -############################### -# constants -tmp_file_prefix=zwe-support-verify-fingerprints -ZWE_VERSION=$(shell_read_json_config "${ZWE_zowe_runtimeDirectory}/manifest.json" 'version' 'version') - -clean_up_tmp_files() { - print_debug "- Clean up temporary files..." - if [ -n "${all_files}" ]; then - rm -f "${all_files}" - fi - if [ -n "${cust_hashes}" ]; then - rm -f "${cust_hashes}" - fi -} - -############################### -# validation -require_java - -if [ -z "${ZWE_VERSION}" ]; then - print_error_and_exit "Error ZWEL0113E: Failed to find Zowe version. Please validate your Zowe directory." "" 113 -fi -if [ ! -f "${ZWE_zowe_runtimeDirectory}/bin/utils/HashFiles.class" ]; then - print_error_and_exit "Error ZWEL0150E: Failed to find file bin/utils/HashFiles.class. Zowe runtimeDirectory is invalid." "" 150 -fi -if [ ! -f "${ZWE_zowe_runtimeDirectory}/fingerprint/RefRuntimeHash-${ZWE_VERSION}.txt" ]; then - print_error_and_exit "Error ZWEL0150E: Failed to find file fingerprint/RefRuntimeHash-${ZWE_VERSION}.txt. Zowe runtimeDirectory is invalid." "" 150 -fi - -############################### -cd "${ZWE_zowe_runtimeDirectory}" - -print_message "- Create Zowe directory file list" -all_files=$(create_tmp_file "${tmp_file_prefix}") -find . -name ./SMPE -prune \ - -o -name "./ZWE*" -prune \ - -o -name ./fingerprint -prune \ - -o -type f -print > "${all_files}" -if [ ! -f "${all_files}" ]; then - print_error " * Error ZWEL0151E: Failed to create temporary file ${all_files}. Please check permission or volume free space." - clean_up_tmp_files - exit 151 -fi -chmod 700 "${all_files}" -print_debug " * File list created as ${all_files}" - -print_message "- Calculate hashes of Zowe files" -cust_hashes=$(create_tmp_file "${tmp_file_prefix}") -result=$(java -cp "${ZWE_zowe_runtimeDirectory}/bin/utils/" HashFiles "${all_files}" | sort > "${cust_hashes}") -code=$? -if [ -f "${cust_hashes}" ]; then - file_size_check=$(wc -l "${cust_hashes}" | awk '{print $1}') -fi -if [ "${code}" -eq 1 -o ! -f "${cust_hashes}" -o "${file_size_check}" -eq 0 ]; then - print_error " * Error ZWEL0151E: Failed to create temporary file ${cust_hashes}. Please check permission or volume free space." - print_error " * Exit code: ${code}" - print_error " * Output:" - if [ -n "${result}" ]; then - print_error "$(padding_left "${result}" " ")" - fi - clean_up_tmp_files - exit 151 -fi -chmod 700 "${cust_hashes}" -print_debug " * Zowe file hashes created as ${cust_hashes}" - -verify_failed= -while read -r step; do - comm_param=$(echo "${step}" | awk '{print $1}') - step_name=$(echo "${step}" | awk '{print $2}') - - print_message "- Find ${step_name} files" - result=$(comm -${comm_param} "${ZWE_zowe_runtimeDirectory}/fingerprint/RefRuntimeHash-${ZWE_VERSION}.txt" "${cust_hashes}") - if [ ${code} -eq 1 ]; then - print_error " * Error ZWEL0151E: Failed to compare hashes of fingerprint/RefRuntimeHash-${ZWE_VERSION}.txt and current." - print_error " * Exit code: ${code}" - print_error " * Output:" - if [ -n "${result}" ]; then - print_error "$(padding_left "${result}" " ")" - fi - clean_up_tmp_files - exit 151 - fi - - cnt=$(printf "${result}" | wc -l | awk '{print $1}') - print_message " * Number of ${step_name} files: ${cnt}" - - if [ ${cnt} -gt 0 ]; then - verify_failed=true - if [ "${ZWE_PRIVATE_LOG_LEVEL_ZWELS}" = "TRACE" ]; then - print_trace " * All ${step_name} files:" - print_trace "${result}" - elif [ "${ZWE_PRIVATE_LOG_LEVEL_ZWELS}" = "DEBUG" ]; then - print_debug " * First 10 ${step_name} files:" "console" - head_10_result=$(echo "${result}" | head -n 10 | awk '{ print $1 }') - print_debug "$(padding_left "${head_10_result}" " ")" "console" - fi - fi -done < 0) { + linesSplit = linesSplit.slice(0, lines); + } + // awk '{ print $1 }' + linesSplit.forEach(line => { + const oneLineSplit = line.split(' '); + returnedOutput += `${oneLineSplit[0]}\n` + }) + } + return returnedOutput; +} + +export function execute(doNotExit: Boolean, javaHome: string): void { + + common.printLevel0Message('Verify Zowe file fingerprints'); + + if (!javaHome) { + javaHome = std.getenv('JAVA_HOME'); + const validJava = javaCI.validateJavaHome(javaHome); + if (!validJava) { + common.printErrorAndExit('Error ZWEL0122E Cannot find java. Please define JAVA_HOME environment variable.', undefined, 122); + } + } + + const tmpFilePrefix = 'zwe-support-verify-fingerprints'; + const zoweRuntime = std.getenv('ZWE_zowe_runtimeDirectory'); + const manifest = `${zoweRuntime}/manifest.json` + + let manifestContent = undefined; + let manifestJson = undefined; + if (fs.fileExists(manifest)) { + manifestContent = xplatform.loadFileUTF8(manifest, xplatform.AUTO_DETECT); + } else { + common.printErrorAndExit(`Error ZWEL0150E: Failed to find file "${manifest}". Zowe runtimeDirectory is invalid.`, undefined, 150); + } + if (manifestContent) { + manifestJson = JSON.parse(manifestContent); + } + if (!manifestContent || !manifestJson.version) { + common.printErrorAndExit("Error ZWEL0113E: Failed to find Zowe version. Please validate your Zowe directory.", undefined, 113); + } + const zoweVersion = manifestJson.version; + + if (!fs.fileExists(`${zoweRuntime}/bin/utils/HashFiles.class`)) { + common.printErrorAndExit(`Error ZWEL0150E: Failed to find file "${zoweRuntime}/bin/utils/HashFiles.class". Zowe runtimeDirectory is invalid.`, undefined, 150); + } + + if (!fs.fileExists(`${zoweRuntime}/fingerprint/RefRuntimeHash-${zoweVersion}.txt`)) { + common.printErrorAndExit(`Error ZWEL0150E: Failed to find file "${zoweRuntime}/fingerprint/RefRuntimeHash-${zoweVersion}.txt". Zowe runtimeDirectory is invalid.`, undefined, 150); + } + + common.printMessage('- Create Zowe directory file list'); + const allFiles = fs.createTmpFile(tmpFilePrefix); + shell.execOutSync('sh', '-c', `cd '${zoweRuntime}' && find . -name ./SMPE -prune -o -name "./ZWE*" -prune -o -name ./fingerprint -prune -o -type f -print > "${allFiles}"`); + if (!fs.fileExists(allFiles)) { + common.printErrorAndExit(`Error ZWEL0151E: Failed to create temporary file "${allFiles}". Please check permission or volume free space.`, undefined, 151); + } + + common.printDebug(` * File list created as ${allFiles}`); + shell.execSync('sh', '-c', `chmod 700 "${allFiles}"`); + + common.printMessage('- Calculate hashes of Zowe files'); + + const customHashes = fs.createTmpFile(tmpFilePrefix); + const javaHash = shell.execOutSync('sh', '-c', `cd '${zoweRuntime}' && '${javaHome}/bin/java' -cp '${zoweRuntime}/bin/utils/' HashFiles '${allFiles}' | sort > '${customHashes}'`); + + if (javaHash.rc != 0 || !fs.fileExists(customHashes) || fs.fileSize(customHashes) < 1) { + common.printError(` * Error ZWEL0151E: Failed to create temporary file ${customHashes}. Please check permission or volume free space.`); + common.printError(` * Exit code: java error code=${javaHash.rc}`) + common.printError(` * file exists=${fs.fileExists(customHashes)}`); + if (fs.fileExists(customHashes)) { + common.printError(` * file size=${fs.fileSize(customHashes)}`); + fs.rmrf(allFiles); + fs.rmrf(customHashes); + } + if (javaHash.out) { + common.printError(stringlib.paddingLeft(javaHash.out, " ")); + } + std.exit(151); + } + + common.printDebug(` * Zowe file hashes created as ${customHashes}`); + shell.execSync('sh', '-c', `chmod 700 "${customHashes}"`); + + let verifyFailed = false; + const logLevel = std.getenv('ZWE_PRIVATE_LOG_LEVEL_ZWELS'); + const COMM = [ + [ 3, 'different' ], + [ 13, 'extra' ], + [ 23, 'missing' ] + ] + + COMM.forEach(commSetting => { + const commParameter = commSetting[0]; + const commStepName = commSetting[1] + common.printMessage(`- Find ${commStepName} files`); + const commResult = shell.execOutSync('sh', '-c', `cd '${zoweRuntime}' && comm -${commParameter} "${zoweRuntime}/fingerprint/RefRuntimeHash-${zoweVersion}.txt" "${customHashes}"`); + if (commResult.rc) { + common.printError(` * Error ZWEL0151E: Failed to compare hashes of fingerprint/RefRuntimeHash-${zoweVersion}.txt and current.`); + common.printError(` * Exit code: ${commResult.rc}`); + if (commResult.out) { + common.printError(` * Output:`); + common.printError(`${stringlib.paddingLeft(commResult.out, " ")}`); + } + fs.rmrf(allFiles); + fs.rmrf(customHashes); + std.exit(151); + } + + if (commResult.out) { + const linesReturned = commResult.out.split("\n").length; + common.printMessage(` * Number of ${commStepName} files: ${linesReturned}`); + if (linesReturned) { + verifyFailed = true; + if (logLevel == 'TRACE' ) { + common.printTrace(` * All ${commStepName} files:`); + common.printTrace(processCommResult(commResult.out, undefined)); + } + if (logLevel == 'DEBUG') { + common.printDebug(` * First 10 ${commStepName} files:`); + common.printDebug(stringlib.paddingLeft(processCommResult(commResult.out, 10)," ")); + } + } + } else { + common.printMessage(` * Number of ${commStepName} files: 0`); + } + }); + + fs.rmrf(allFiles); + fs.rmrf(customHashes); + + if (verifyFailed) { + common.printMessage(""); + common.printError('Error ZWEL0181E: Failed to verify Zowe file fingerprints.'); + if (doNotExit != true) { + std.exit(181); + } + } else { + common.printLevel1Message('Zowe file fingerprints verification passed.'); + } +} diff --git a/bin/libs/fs.ts b/bin/libs/fs.ts index 632bb2cbd7..1311395725 100644 --- a/bin/libs/fs.ts +++ b/bin/libs/fs.ts @@ -328,3 +328,15 @@ export function areDirectoriesSame(dir1: string, dir2: string): boolean { let abs2 = convertToAbsolutePath(dir2); return (abs1 === abs2) && abs1 !== undefined; } + +export function fileSize(file: string): number { + common.printDebug(`fs.fileSize path="${file}"`); + if (file && typeof(file) == 'string') { + const result = zos.zstat(file); + common.printDebug(`fs.fileSize result="${JSON.stringify(result)}"`); + if (result[1] == 0) { + return result[0].size; + } + } + return -1; +} diff --git a/bin/libs/java.ts b/bin/libs/java.ts index 896d5a0c20..f7fa161119 100644 --- a/bin/libs/java.ts +++ b/bin/libs/java.ts @@ -13,11 +13,10 @@ import * as std from 'cm_std'; import * as os from 'cm_os'; import * as fs from './fs'; import * as common from './common'; +import * as javaCI from './java_ci' import * as shell from './shell'; import * as config from './config'; -const JAVA_MIN_VERSION=17; - export function ensureJavaIsOnPath(): void { let path=std.getenv('PATH') || '/bin:.:/usr/bin'; let javaHome=std.getenv('JAVA_HOME'); @@ -30,7 +29,7 @@ export function readConfigJavaHome(configList?: string, skipValidate?: boolean): const zoweConfig = config.getZoweConfig(); if (zoweConfig && zoweConfig.java && zoweConfig.java.home) { if (!skipValidate) { - if (!validateJavaHome(zoweConfig.java.home)) { + if (!javaCI.validateJavaHome(zoweConfig.java.home)) { return ''; } } @@ -79,67 +78,6 @@ export function requireJava() { _javaCheckComplete = true; } -export function validateJavaHome(javaHome:string|undefined=std.getenv("JAVA_HOME")): boolean { - if (!javaHome) { - common.printError("Cannot find java. Please define JAVA_HOME environment variable."); - return false; - } - if (!fs.fileExists(fs.resolvePath(javaHome,`/bin/java`))) { - common.printError(`JAVA_HOME: ${javaHome}/bin does not point to a valid install of Java.`); - return false; - } - - let execReturn = shell.execErrSync(fs.resolvePath(javaHome,`/bin/java`), `-version`); - const version = execReturn.err; - if (execReturn.rc != 0) { - common.printError(`Java version check failed with return code: ${execReturn.rc}: ${version}`); - return false; - } - - try { - let index = 0; - let javaVersionShort; - let versionLines = (version as string).split('\n'); // valid because of above rc check - for (let i = 0; i < versionLines.length; i++) { - if ((index = versionLines[i].indexOf('java version')) != -1) { - //format of: java version "1.8.0_321" OR java version "17.0.10" 2024-01-02 - javaVersionShort = versionLines[i].substring(index+('java version'.length)+2); - javaVersionShort = javaVersionShort.replace(/"/g, ''); - break; - } else if ((index = versionLines[i].indexOf('openjdk version')) != -1) { - javaVersionShort=versionLines[i].substring(index+('openjdk version'.length)+2, versionLines[i].length-1); - break; - } - } - if (!javaVersionShort){ - common.printError("could not find java version"); - return false; - } - let versionParts = javaVersionShort.split('.'); - const javaMajorVersion=Number(versionParts[0]); - const javaMinorVersion=Number(versionParts[1]); - - let tooLow=false; - if (javaMajorVersion !== 1 && javaMajorVersion < JAVA_MIN_VERSION) { - tooLow=true; - } - if (javaMajorVersion === 1 && javaMinorVersion < JAVA_MIN_VERSION) { - tooLow=true; - } - - if (tooLow) { - common.printError(`Java ${javaVersionShort} is less than the minimum level required of Java ${JAVA_MIN_VERSION}.`); - return false; - } - - common.printDebug(`Java ${javaVersionShort} is supported.`); - common.printDebug(`Java check is successful.`); - return true; - } catch (e) { - return false; - } -} - export function getJavaPkcs12KeystoreFlag(javaHome:string|undefined=std.getenv("JAVA_HOME")): string { if (!javaHome) { common.printError("Cannot find java. Please define JAVA_HOME environment variable."); diff --git a/bin/libs/java_ci.ts b/bin/libs/java_ci.ts new file mode 100644 index 0000000000..de39e5e8df --- /dev/null +++ b/bin/libs/java_ci.ts @@ -0,0 +1,78 @@ +/* + 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 std from 'cm_std'; +import * as fs from './fs'; +import * as common from './common'; +import * as shell from './shell'; + +const JAVA_MIN_VERSION=17; + +export function validateJavaHome(javaHome:string|undefined=std.getenv("JAVA_HOME")): boolean { + if (!javaHome) { + common.printError("Cannot find java. Please define JAVA_HOME environment variable."); + return false; + } + if (!fs.fileExists(fs.resolvePath(javaHome,`/bin/java`))) { + common.printError(`JAVA_HOME: ${javaHome}/bin does not point to a valid install of Java.`); + return false; + } + + let execReturn = shell.execErrSync(fs.resolvePath(javaHome,`/bin/java`), `-version`); + const version = execReturn.err; + if (execReturn.rc != 0) { + common.printError(`Java version check failed with return code: ${execReturn.rc}: ${version}`); + return false; + } + + try { + let index = 0; + let javaVersionShort; + let versionLines = (version as string).split('\n'); // valid because of above rc check + for (let i = 0; i < versionLines.length; i++) { + if ((index = versionLines[i].indexOf('java version')) != -1) { + //format of: java version "1.8.0_321" OR java version "17.0.10" 2024-01-02 + javaVersionShort = versionLines[i].substring(index+('java version'.length)+2); + javaVersionShort = javaVersionShort.replace(/"/g, ''); + break; + } else if ((index = versionLines[i].indexOf('openjdk version')) != -1) { + javaVersionShort=versionLines[i].substring(index+('openjdk version'.length)+2, versionLines[i].length-1); + break; + } + } + if (!javaVersionShort){ + common.printError("could not find java version"); + return false; + } + let versionParts = javaVersionShort.split('.'); + const javaMajorVersion=Number(versionParts[0]); + const javaMinorVersion=Number(versionParts[1]); + + let tooLow=false; + if (javaMajorVersion !== 1 && javaMajorVersion < JAVA_MIN_VERSION) { + tooLow=true; + } + if (javaMajorVersion === 1 && javaMinorVersion < JAVA_MIN_VERSION) { + tooLow=true; + } + + if (tooLow) { + common.printError(`Java ${javaVersionShort} is less than the minimum level required of Java ${JAVA_MIN_VERSION}.`); + return false; + } + + common.printDebug(`Java ${javaVersionShort} is supported.`); + common.printDebug(`Java check is successful.`); + return true; + } catch (e) { + return false; + } +} diff --git a/playbooks/roles/verify/tasks/main.yml b/playbooks/roles/verify/tasks/main.yml index 2651ceea99..845d42c33d 100644 --- a/playbooks/roles/verify/tasks/main.yml +++ b/playbooks/roles/verify/tasks/main.yml @@ -53,6 +53,7 @@ ZOWE_INSTANCE_ID: "{{ zowe_instance_id }}" ZOWE_ZLUX_HTTPS_PORT: "{{ zowe_zlux_port }}" ZOWE_API_MEDIATION_GATEWAY_HTTP_PORT: "{{ zowe_apiml_gateway_port }}" + ZOWE_JAVA_HOME: "{{ zos_java_home }}" DEBUG: "{{ zowe_sanity_test_debug_mode | default('') }}" # # this should be commented out for live tests # NODE_DEBUG: "http,https" diff --git a/tests/sanity/test/install/test-installed-files.js b/tests/sanity/test/install/test-installed-files.js index 976bcfd202..58170089c6 100644 --- a/tests/sanity/test/install/test-installed-files.js +++ b/tests/sanity/test/install/test-installed-files.js @@ -43,7 +43,12 @@ describe('verify installed files', function() { it('fingerprint should match', async function() { // IMPORT: After 'source' the profile, JAVA_HOME environment variable must exist // note the --config assumes the instance dir, which is set in ansible playbooks - const fingerprintStdout = await sshHelper.executeCommandWithNoError(`touch ~/.profile && . ~/.profile && ${process.env.ZOWE_ROOT_DIR}/bin/zwe support verify-fingerprints --config /ZOWE/tmp/.zowe/zowe.yaml`); + // if we have ZOWE_JAVA_HOME in our env, just use that. + let OPTIONAL_JAVA_HOME = ''; + if (process.env.ZOWE_JAVA_HOME) { + OPTIONAL_JAVA_HOME = `JAVA_HOME=${process.env.ZOWE_JAVA_HOME} `; + } + const fingerprintStdout = await sshHelper.executeCommandWithNoError(`touch ~/.profile && . ~/.profile && ${OPTIONAL_JAVA_HOME} ${process.env.ZOWE_ROOT_DIR}/bin/zwe support verify-fingerprints`); debug('fingerprint show result:', fingerprintStdout); addContext(this, { title: 'fingerprint show result',