Skip to content

Commit f7c9a85

Browse files
committed
initial error handling for SSH
1 parent 7957551 commit f7c9a85

File tree

2 files changed

+102
-8
lines changed

2 files changed

+102
-8
lines changed

__tests__/api/BundlePush/BundlePusher.test.ts

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,31 @@ const DEFAULT_PARAMTERS: IHandlerParameters = {
3030
response: {
3131
data: {
3232
setMessage: jest.fn((setMsgArgs) => {
33-
expect("" + setMsgArgs).toMatchSnapshot();
33+
throw new Error("Unexpected use of setMessage in Mock: " + setMsgArgs.toString());
3434
}),
3535
setObj: jest.fn((setObjArgs) => {
36-
expect(setObjArgs).toMatchSnapshot();
36+
throw new Error("Unexpected use of setObj in Mock: " + setObjArgs.toString());
3737
})
3838
},
3939
console: {
4040
log: jest.fn((logs) => {
41-
expect("" + logs).toMatchSnapshot();
41+
consoleText += logs.toString();
4242
}),
4343
error: jest.fn((errors) => {
44-
expect("" + errors).toMatchSnapshot();
44+
throw new Error("Unexpected use of error log in Mock: " + errors.toString());
4545
}),
4646
errorHeader: jest.fn(() => undefined)
4747
},
4848
progress: {
4949
startBar: jest.fn((parms) => undefined),
5050
endBar: jest.fn(() => undefined)
51-
}
51+
},
52+
consoleText: ""
5253
} as any,
5354
definition: PushBundleDefinition.PushBundleDefinition,
5455
fullDefinition: PushBundleDefinition.PushBundleDefinition,
5556
};
57+
let consoleText = "";
5658

5759
// Initialise xml2json before mocking anything
5860
const parser = require("xml2json");
@@ -87,6 +89,7 @@ describe("BundlePusher01", () => {
8789
}
8890
});
8991
uploadSpy = jest.spyOn(Upload, "dirToUSSDirRecursive").mockImplementation(() => ({}));
92+
consoleText = "";
9093
});
9194
afterEach(() => {
9295
jest.restoreAllMocks();
@@ -355,6 +358,40 @@ describe("BundlePusher01", () => {
355358
expect(readSpy).toHaveBeenCalledTimes(1);
356359
expect(uploadSpy).toHaveBeenCalledTimes(1);
357360
});
361+
it("should handle failure of remote npm install", async () => {
362+
shellSpy.mockImplementation((session: any, cmd: string, dir: string, stdoutHandler: (data: string) => void) => {
363+
if (cmd.indexOf("npm install") > -1) {
364+
stdoutHandler("Injected stdout error message");
365+
}
366+
else {
367+
return true;
368+
}
369+
});
370+
existsSpy.mockImplementation((data: string) => {
371+
if (data.indexOf(".zosattributes") > -1) {
372+
return false;
373+
}
374+
if (data.indexOf("package.json") > -1) {
375+
return true;
376+
}
377+
});
378+
379+
await runPushTestWithError("__tests__/__resources__/ExampleBundle01", false,
380+
"A problem occurred attempting to run 'npm install' in remote directory '/u/ThisDoesNotExist/12345678'. " +
381+
"Problem is: The output from the remote command implied that an error occurred.");
382+
383+
expect(consoleText).toContain("Injected stdout error message");
384+
expect(zosMFSpy).toHaveBeenCalledTimes(1);
385+
expect(sshSpy).toHaveBeenCalledTimes(1);
386+
expect(createSpy).toHaveBeenCalledTimes(1);
387+
expect(listSpy).toHaveBeenCalledTimes(1);
388+
expect(shellSpy).toHaveBeenCalledTimes(1);
389+
expect(membersSpy).toHaveBeenCalledTimes(0);
390+
expect(submitSpy).toHaveBeenCalledTimes(0);
391+
expect(existsSpy).toHaveBeenCalledTimes(2);
392+
expect(readSpy).toHaveBeenCalledTimes(1);
393+
expect(uploadSpy).toHaveBeenCalledTimes(1);
394+
});
358395
it("should handle error with remote bundle deploy", async () => {
359396
submitSpy.mockImplementationOnce(() => { throw new Error("Injected deploy error"); });
360397

@@ -388,6 +425,41 @@ describe("BundlePusher01", () => {
388425
expect(readSpy).toHaveBeenCalledTimes(1);
389426
expect(uploadSpy).toHaveBeenCalledTimes(1);
390427
});
428+
it("should run to completion with verbose output", async () => {
429+
const parms = getCommonParmsForPushTests();
430+
parms.arguments.verbose = true;
431+
shellSpy.mockImplementation((session: any, cmd: string, dir: string, stdoutHandler: (data: string) => void) => {
432+
stdoutHandler("Injected stdout shell message");
433+
});
434+
existsSpy.mockImplementation((data: string) => {
435+
if (data.indexOf(".zosattributes") > -1) {
436+
return false;
437+
}
438+
if (data.indexOf("package.json") > -1) {
439+
return true;
440+
}
441+
});
442+
443+
await runPushTest("__tests__/__resources__/ExampleBundle01", false, "PUSH operation completed.", parms);
444+
445+
expect(consoleText).toContain("Making remote bundle directory");
446+
expect(consoleText).toContain("Accessing contents of remote bundle directory");
447+
expect(consoleText).toContain("Uploading the bundle to the remote bundle directory");
448+
expect(consoleText).toContain("Running npm install for the remote bundle");
449+
expect(consoleText).toContain("Injected stdout shell message");
450+
expect(consoleText).toContain("Deploying the bundle to CICS");
451+
expect(consoleText).toContain("Deployed existing bundle to CICS");
452+
expect(zosMFSpy).toHaveBeenCalledTimes(1);
453+
expect(sshSpy).toHaveBeenCalledTimes(1);
454+
expect(listSpy).toHaveBeenCalledTimes(1);
455+
expect(createSpy).toHaveBeenCalledTimes(1);
456+
expect(shellSpy).toHaveBeenCalledTimes(1);
457+
expect(membersSpy).toHaveBeenCalledTimes(2);
458+
expect(submitSpy).toHaveBeenCalledTimes(1);
459+
expect(existsSpy).toHaveBeenCalledTimes(2);
460+
expect(readSpy).toHaveBeenCalledTimes(1);
461+
expect(uploadSpy).toHaveBeenCalledTimes(1);
462+
});
391463
});
392464

393465
async function runPushTestWithError(localBundleDir: string, overwrite: boolean, errorText: string, parmsIn?: IHandlerParameters) {
@@ -411,8 +483,14 @@ async function runPushTestWithError(localBundleDir: string, overwrite: boolean,
411483
expect(err.message).toContain(errorText);
412484
}
413485

414-
async function runPushTest(localBundleDir: string, overwrite: boolean, expectedResponse: string) {
415-
const parms = getCommonParmsForPushTests();
486+
async function runPushTest(localBundleDir: string, overwrite: boolean, expectedResponse: string, parmsIn?: IHandlerParameters) {
487+
let parms: IHandlerParameters;
488+
if (parmsIn === undefined) {
489+
parms = getCommonParmsForPushTests();
490+
}
491+
else {
492+
parms = parmsIn;
493+
}
416494
parms.arguments.overwrite = overwrite;
417495

418496
let err: Error;

src/api/BundlePush/BundlePusher.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ export class BundlePusher {
243243
}
244244

245245
private sshOutput(data: string) {
246+
// If verbose output is requested then log SSH output directly to the console
247+
if (this.params.arguments.verbose) {
248+
this.params.response.console.log(data);
249+
}
250+
246251
this.sshOutputText += data;
247252
}
248253

@@ -287,6 +292,17 @@ export class BundlePusher {
287292
try {
288293
this.sshOutputText = "";
289294
const shell = await Shell.executeSshCwd(sshSession, sshCommand, directory, this.sshOutput.bind(this));
295+
296+
// Did the SSH command work? It's unclear how to tell, but for starters let's look for the word
297+
// 'error' in the output text.
298+
if (this.sshOutputText.toUpperCase().indexOf("ERROR ") > -1) {
299+
// if we've not already logged the output, log it now
300+
if (this.params.arguments.verbose !== true)
301+
{
302+
this.params.response.console.log(this.sshOutputText);
303+
}
304+
throw new Error("The output from the remote command implied that an error occurred.");
305+
}
290306
}
291307
catch (error) {
292308
throw new Error("A problem occurred attempting to run '" + sshCommand + "' in remote directory '" + directory +
@@ -330,7 +346,7 @@ export class BundlePusher {
330346
this.progressBar.statusMessage = status;
331347

332348
if (this.params.arguments.verbose) {
333-
this.params.response.console.log(status);
349+
this.params.response.console.log(status + "\n");
334350
}
335351
}
336352
}

0 commit comments

Comments
 (0)