Skip to content

Commit

Permalink
Merge branch 'v2.x/staging' into bugfix/installation
Browse files Browse the repository at this point in the history
Signed-off-by: Timothy Gerstel <[email protected]>
  • Loading branch information
timgerstel committed May 29, 2024
2 parents 4faccd7 + e8ae277 commit 2e20b1c
Show file tree
Hide file tree
Showing 16 changed files with 1,734 additions and 137 deletions.
3 changes: 2 additions & 1 deletion src/actions/InstallActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Copyright Contributors to the Zowe Project.
*/

import { InstallationArgs } from '../renderer/components/stages/installation/installationSlice';
import { IIpcConnectionArgs, IResponse } from '../types/interfaces';
import { FTPInstallation, CLIInstallation } from './InstallationHandler';

Expand Down Expand Up @@ -42,7 +43,7 @@ export class InstallActions {

runInstallation (
connectionArgs: IIpcConnectionArgs,
installationArgs: {installationDir: string, installationType: string, userUploadedPaxPath: string},
installationArgs: InstallationArgs,
version: string, zoweConfig: any, skipDownload: boolean): Promise<IResponse> {
return this.strategy.runInstallation(connectionArgs, installationArgs, version, zoweConfig, skipDownload);
}
Expand Down
144 changes: 133 additions & 11 deletions src/actions/InstallationHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,27 @@ import { app } from 'electron';
import { DataType, FileTransfer } from "../services/FileTransfer";
import path from "path/posix";
import { Script } from "../services/RunScript";
import { stringify } from 'yaml';
import { parse, stringify } from 'yaml';
import { IIpcConnectionArgs, IResponse } from '../types/interfaces';
import { ProgressStore } from "../storage/ProgressStore";
import * as fs from 'fs';
import { ConfigurationStore } from '../storage/ConfigurationStore';
import { InstallationArgs } from '../renderer/components/stages/installation/installationSlice';

class Installation {

public async runInstallation (
connectionArgs: IIpcConnectionArgs,
installationArgs: {installationDir: string, installationType: string, userUploadedPaxPath: string},
installationArgs: InstallationArgs,
version: string,
zoweConfig: any,
skipDownload: boolean
): Promise<IResponse> {
const currentConfig: any = ConfigurationStore.getConfig();
const SMPE_INSTALL: boolean = installationArgs.installationType === "smpe";
const savingResult = await this.generateYamlFile(zoweConfig);
if (!savingResult.status) {
console.log("failed to save yaml");
return savingResult;
}

Expand Down Expand Up @@ -102,6 +106,114 @@ class Installation {
return {status: false, details: `Error unpaxing Zowe archive: ${unpax.details}`};
}
}
let yamlObj
let zoweRuntimePath = installationArgs.installationType === "smpe" ? installationArgs.installationDir : installationArgs.installationDir + "/runtime";
let readPaxYamlAndSchema = await this.readExampleYamlAndSchema(connectionArgs, zoweRuntimePath);
if(readPaxYamlAndSchema.details.yaml){
const parseExampleYamlFromPax = function(catPath: string){
const jobOutputSplit = JSON.stringify(readPaxYamlAndSchema.details.yaml).split(`cat ${catPath}\\r\\n`)
if(jobOutputSplit[1]){
const trimmedYamlSchema = jobOutputSplit[1].split(`+ echo 'Script finished.'`)[0].split(`Script finished.`);
// console.log("\n\n *** trimmedYamlSchema[0]: ", trimmedYamlSchema[0].replaceAll(`\\r\\n`, `\r\n`).replaceAll(`\\"`, `"`));
return trimmedYamlSchema[0].replaceAll(`\\r\\n`, `\r\n`).replaceAll(`\\"`, `"`);
}
return "";
}
const yamlFromPax = parseExampleYamlFromPax(`${zoweRuntimePath}/example-zowe.yaml`);
if(yamlFromPax){
try {
yamlObj = parse(yamlFromPax);
if (currentConfig) {
console.log("current config:", JSON.stringify(currentConfig));
// console.log("yamlObj: ", JSON.stringify(yamlObj));
yamlObj = {...currentConfig, ...yamlObj}
console.log("merged yamlObj: ", JSON.stringify(yamlObj));
}
if (yamlObj.zowe.runtimeDirectory === undefined && installationArgs.installationDir) {
yamlObj.zowe.runtimeDirectory = installationArgs.installationDir;
}
if (yamlObj.zowe.workspaceDirectory === undefined && installationArgs.workspaceDir) {
yamlObj.zowe.workspaceDirectory = installationArgs.workspaceDir;
}
if (yamlObj.zowe.logDirectory === undefined && installationArgs.logDir) {
yamlObj.zowe.logDirectory = installationArgs.logDir;
}
if (yamlObj.zowe.extensionDirectory === undefined && installationArgs.extensionDir) {
yamlObj.zowe.extensionDirectory = installationArgs.extensionDir;
}
if (yamlObj.zowe.rbacProfileIdentifier === undefined && installationArgs.rbacProfile) {
yamlObj.zowe.rbacProfileIdentifier = installationArgs.rbacProfile;
}
if (yamlObj.zowe.job.name === undefined && installationArgs.jobName) {
yamlObj.zowe.job.name = installationArgs.jobName;
}
if (yamlObj.zowe.job.prefix === undefined && installationArgs.jobPrefix) {
yamlObj.zowe.job.prefix = installationArgs.jobPrefix;
}
if (yamlObj.zowe.cookieIdentifier === undefined && installationArgs.cookieId) {
yamlObj.zowe.cookieIdentifier = installationArgs.cookieId;
}
if (yamlObj.java.home === undefined && installationArgs.javaHome) {
yamlObj.java.home = installationArgs.javaHome;
}
if (yamlObj.node.home === undefined && installationArgs.nodeHome) {
yamlObj.node.home = installationArgs.nodeHome;
}
if (yamlObj.zOSMF.host === undefined && installationArgs.zosmfHost) {
yamlObj.zOSMF.host = installationArgs.zosmfHost;
}
if (yamlObj.zOSMF.port === undefined && installationArgs.zosmfPort) {
yamlObj.zOSMF.port = installationArgs.zosmfPort;
}
if (yamlObj.zOSMF.applId === undefined && installationArgs.zosmfApplId) {
yamlObj.zOSMF.applId = installationArgs.zosmfApplId;
}
if (zoweConfig) {
yamlObj = {...yamlObj, ...zoweConfig};
}
console.log('Setting merged yaml:', JSON.stringify(yamlObj));
ConfigurationStore.setConfig(yamlObj);
} catch(e) {
console.log('error parsing example-zowe.yaml:', e);
}
} else {
console.log("no yaml found from pax");
}

//No reason not to always set schema to latest if user is re-running installation
if(readPaxYamlAndSchema.details.yamlSchema && readPaxYamlAndSchema.details.serverCommon){
const parseSchemaFromPax = function(inputString: string, catPath: string){
const jobOutputSplit = inputString.split(`cat ${catPath}\\r\\n`)
if(jobOutputSplit[1]){
const trimmedYamlSchema = jobOutputSplit[1].split(`Script finished.`)[0].split(`Script finished.`);
return trimmedYamlSchema[0].replaceAll(`\\r\\n`, `\r\n`).replaceAll(`\\"`, `"`).replaceAll(`\\\\"`, `\\"`);
}
return "";
}
try {
let yamlSchema = JSON.parse(parseSchemaFromPax(JSON.stringify(readPaxYamlAndSchema.details.yamlSchema), `${zoweRuntimePath}/schemas/zowe-yaml-schema.json`));
const serverCommon = JSON.parse(parseSchemaFromPax(JSON.stringify(readPaxYamlAndSchema.details.serverCommon), `${zoweRuntimePath}/schemas/server-common.json`));
// console.log('yaml schema:', parseSchemas(JSON.stringify(readPaxYamlAndSchema.details.yamlSchema), `${zoweRuntimePath}/schemas/zowe-yaml-schema.json`));
// console.log('server common', parseSchemas(JSON.stringify(readPaxYamlAndSchema.details.serverCommon), `${zoweRuntimePath}/schemas/server-common.json`));
if(yamlSchema && serverCommon){
// FIXME: Link schema by $ref properly - https://jsonforms.io/docs/ref-resolving
// Without these, AJV does not properly find $refs in the schema and therefore validation cannot occur
yamlSchema.properties.zowe.properties.setup.properties.dataset.properties.parmlibMembers.properties.zis = serverCommon.$defs.datasetMember;
yamlSchema.properties.zowe.properties.setup.properties.certificate.properties.pkcs12.properties.directory = serverCommon.$defs.path;
yamlSchema.$id = serverCommon.$id;
if(yamlSchema.$defs?.networkSettings?.properties?.server?.properties?.listenAddresses?.items){
delete yamlSchema.$defs?.networkSettings?.properties?.server?.properties?.listenAddresses?.items?.ref;
yamlSchema.$defs.networkSettings.properties.server.properties.listenAddresses.items = serverCommon.$defs.ipv4
}
console.log('Setting schema from runtime dir:', JSON.stringify(yamlSchema));
ConfigurationStore.setSchema(yamlSchema);
}
} catch (e) {
console.log('error setting schema from pax:', e);
}
}

}

let installation;
if (installationArgs.installationType !== "smpe") {
Expand All @@ -121,7 +233,7 @@ class Installation {
let initMvs;
if(installation.status){
console.log("running zwe init mvs...");
initMvs = await this.initMVS(connectionArgs, SMPE_INSTALL ? installationArgs.installationDir : installationArgs.installationDir);
initMvs = await this.initMVS(connectionArgs, installationArgs.installationDir);
ProgressStore.set('installation.initMVS', initMvs.status);
} else {
initMvs = {status: false, details: `zwe install step failed, unable to run zwe init mvs.`}
Expand All @@ -132,7 +244,7 @@ class Installation {
return {status: false, details: `Error running zwe init mvs: ${initMvs.details}`};
}

return {status: download.status && uploadYaml.status && upload.status && unpax.status && installation.status && initMvs.status, details: 'Zowe install successful.'};
return {status: download.status && uploadYaml.status && upload.status && unpax.status && installation.status && initMvs.status, details: {message: 'Zowe install successful.', mergedYaml: yamlObj}};
} catch (error) {
return {status: false, details: error.message};
}
Expand Down Expand Up @@ -263,13 +375,8 @@ class Installation {
return {status: true, details: ''};
}

async uploadYaml(connectionArgs: IIpcConnectionArgs, installDir: string) {
const tempPath = path.join(app.getPath("temp"), "zowe.yaml");
const filePath = path.join(installDir, "zowe.yaml");
await new FileTransfer().upload(connectionArgs, tempPath, filePath, DataType.BINARY)
const script = `chtag -t -c ISO8859-1 ${installDir}/zowe.yaml`;
const result = await new Script().run(connectionArgs, script);
return {status: result.rc === 0, details: result.jobOutput}
async uploadYaml(connectionArgs: IIpcConnectionArgs, installDir: string): Promise<IResponse> {
return {status: false, details: 'Method not implemented.'}
}

async downloadPax(version: string): Promise<IResponse> {
Expand All @@ -291,6 +398,10 @@ class Installation {
async initMVS(connectionArgs: IIpcConnectionArgs, installDir: string): Promise<IResponse> {
return {status: false, details: 'Method not implemented.'}
}

async readExampleYamlAndSchema(connectionArgs: IIpcConnectionArgs, installDir: string): Promise<IResponse>{
return {status: false, details: 'Method not implemented.'}
}

}

Expand All @@ -299,6 +410,7 @@ export class FTPInstallation extends Installation {
async uploadYaml(connectionArgs: IIpcConnectionArgs, installDir: string) {
const tempPath = path.join(app.getPath("temp"), "zowe.yaml");
const filePath = path.join(installDir, "zowe.yaml");
console.log(`Uploading ${tempPath} to ${filePath}`)
await new FileTransfer().upload(connectionArgs, tempPath, filePath, DataType.BINARY)
const script = `chtag -t -c ISO8859-1 ${installDir}/zowe.yaml`;
const result = await new Script().run(connectionArgs, script);
Expand Down Expand Up @@ -348,6 +460,16 @@ export class FTPInstallation extends Installation {
return {status: result.rc === 0, details: result.jobOutput}
}

async readExampleYamlAndSchema(connectionArgs: IIpcConnectionArgs, installDir: string){
const catYaml = `cat ${installDir}/example-zowe.yaml`;
const yamlResult = await new Script().run(connectionArgs, catYaml);
const catYamlSchema = `cat ${installDir}/schemas/zowe-yaml-schema.json`;
const yamlSchemaResult = await new Script().run(connectionArgs, catYamlSchema);
const catCommonSchema = `cat ${installDir}/schemas/server-common.json`;
const commonSchemaResult = await new Script().run(connectionArgs, catCommonSchema);
return {status: yamlResult.rc === 0 && yamlSchemaResult.rc == 0 && commonSchemaResult.rc == 0, details: {yaml: yamlResult.jobOutput, yamlSchema: yamlSchemaResult.jobOutput, serverCommon: commonSchemaResult.jobOutput}}
}

async checkInstallData() {
// FIXME: Refine installation data validation
}
Expand Down
15 changes: 13 additions & 2 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ const createWindow = (): void => {
});

ipcMain.handle('get-config', async (event) => {
const res: any = await PlanningActions.getConfig();
return res;
const res: any = await ConfigurationStore.getConfig();
return {status: true, details: res};
});

ipcMain.handle('set-schema', async (event, schema: any) => {
Expand All @@ -102,6 +102,12 @@ const createWindow = (): void => {
});


ipcMain.handle('get-schema', async (event, schema: any) => {
const res: any = await ConfigurationStore.getSchema();
return {status: true, details: res};
});


ipcMain.handle('get-config-by-key', async (_event, key: string) => {
const res = await ConfigurationStore.getConfigByKey(key);
return res;
Expand All @@ -112,6 +118,11 @@ const createWindow = (): void => {
return res;
});

ipcMain.handle('set-config-by-key-no-validate', async (_event, key: string, value) => {
const res = await ConfigurationStore.setConfigByKeyNoValidate(key, value);
return res;
});

ipcMain.handle('get-zowe-version', async () => {
const res = await PlanningActions.getZoweVersion();
return res;
Expand Down
19 changes: 15 additions & 4 deletions src/renderer/components/common/Stepper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ export default function HorizontalLinearStepper({stages, initialization}:{stages
completeProgress.reviewStatus
];

const subStageProgressStatus = [
const [subStageProgressStatus, setProgressStatus] = useState([
completeProgress.datasetInstallationStatus,
completeProgress.networkingStatus,
completeProgress.apfAuthStatus,
completeProgress.securityStatus,
completeProgress.certificateStatus,
completeProgress.launchConfigStatus
]
])

const TYPE_YAML = "yaml";
const TYPE_JCL = "jcl";
Expand All @@ -76,9 +76,18 @@ export default function HorizontalLinearStepper({stages, initialization}:{stages
const [editorContent, setEditorContent] = useState('');

useEffect(() => {
const mvsCompleteListener = (completed: boolean) => {
setProgressStatus([true, completeProgress.networkingStatus,
completeProgress.apfAuthStatus,
completeProgress.securityStatus,
completeProgress.certificateStatus,
completeProgress.launchConfigStatus])
};
eventDispatcher.on('updateActiveStep', updateActiveStepListener);
eventDispatcher.on('initMvsComplete', mvsCompleteListener);
return () => {
eventDispatcher.off('updateActiveStep', updateActiveStepListener);
eventDispatcher.off('initMvsComplete', mvsCompleteListener);
};
}, []);

Expand Down Expand Up @@ -169,7 +178,9 @@ export default function HorizontalLinearStepper({stages, initialization}:{stages

setActiveStep(newActiveStep);
const newSubStep = isSubStep ? subStepIndex : 0;
setActiveSubStep(newSubStep);
if((subStepIndex > 0 && subStageProgressStatus[0] === true) || subStepIndex === 0){ //only allow substages after installation to be navigated to if init mvs has been completed
setActiveSubStep(newSubStep);
}
}

const getStepIcon = (error: any, stageId: number, isSubStep?: boolean, subStepId?: number) => {
Expand Down Expand Up @@ -309,7 +320,7 @@ export default function HorizontalLinearStepper({stages, initialization}:{stages
}
{stages[activeStep] && stages[activeStep].isSkippable &&
<Button
disabled={isNextStepEnabled}
disabled={stages[activeStep] && stages[activeStep].subStages ? !stages[activeStep].subStages[activeSubStep].isSkippable : !stages[activeStep].isSkippable}
variant="contained"
sx={{ textTransform: 'none', mr: 1 }}
onClick={() => handleSkip()}
Expand Down
5 changes: 4 additions & 1 deletion src/renderer/components/configuration-wizard/Wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ import InitApfAuth from '../stages/InitApfAuth';
import Networking from '../stages/Networking';
import Vsam from '../stages/Vsam';
import LaunchConfig from '../stages/LaunchConfig';
import { getProgress } from '../stages/progress/StageProgressStatus';

const mvsDatasetInitProgress = getProgress('datasetInstallationStatus');

export const stages = [
{id: 0, label: 'Connection', component: <Connection/>, hasJCL: false, isSkippable: false, isSkipped: false, hasOutput: false, steps: 1, nextButton: 'Continue', statusKey: 'connectionStatus'},
{id: 1, label: 'Planning', component: <Planning/>, hasJCL: false, isSkippable: false, isSkipped: false, hasOutput: true, steps: 3, nextButton: 'Continue to Installation Options', statusKey: 'planningStatus'},
{id: 2, label: 'Installation Type', component: <InstallationType/>, hasJCL: false, isSkippable: false, isSkipped: false, hasOutput: false, steps: 1, nextButton: 'Continue to Components Installation', statusKey: 'installationTypeStatus'},
{id: 3, label: 'Initialization', component: <Initialization/>, hasJCL: true, isSkippable: true, isSkipped: false, hasYaml: true, hasOutput: true, steps: 1, subStages: [
{id: 0, label: 'Installation', component: <Installation/>, hasJCL: true, isSkippable: true, isSkipped: false, hasYaml: true, hasOutput: true, steps: 1, nextButton: 'Continue to Network Setup', statusKey: 'datasetInstallationStatus'},
{id: 0, label: 'Installation', component: <Installation/>, hasJCL: true, isSkippable: mvsDatasetInitProgress ?? false, isSkipped: false, hasYaml: true, hasOutput: true, steps: 1, nextButton: 'Continue to Network Setup', statusKey: 'datasetInstallationStatus'},
{id: 1, label: 'Networking', component: <Networking/>, hasJCL: true, isSkippable: true, isSkipped: false, hasYaml: true, hasOutput: true, steps: 1, nextButton: 'Continue to APF Auth Setup', statusKey: 'networkingStatus'},
{id: 2, label: 'APF Auth', component: <InitApfAuth/>, hasJCL: true, isSkippable: true, isSkipped: false, hasYaml: true, hasOutput: true, steps: 1, nextButton: 'Continue to Security Setup', statusKey: 'apfAuthStatus'},
{id: 3, label: 'Security', component: <Security/>, hasJCL: true, isSkippable: true, isSkipped: false, hasYaml: true, hasOutput: true, steps: 1, nextButton: 'Continue to Certificates Setup', statusKey: 'securityStatus'},
Expand Down
Loading

0 comments on commit 2e20b1c

Please sign in to comment.