Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Daemon Communication Mechanism #1308

Merged
merged 32 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
478ecc4
WIP Use mio for sockets and named pipes
t1m0thyj Feb 4, 2022
8a3ba53
Hopefully more progress for Windows
t1m0thyj Feb 4, 2022
8792232
Get named pipes working
t1m0thyj Feb 4, 2022
c12963b
Make named_pipe dependency Windows-only
t1m0thyj Feb 4, 2022
995dc62
Make named_pipe dependency Windows-only pt 2
t1m0thyj Feb 4, 2022
24c88b8
Fix number of parameters to format!
t1m0thyj Feb 4, 2022
637aef9
Use BufStream to allow simultaneous read/write
t1m0thyj Feb 7, 2022
71ea7f8
Hopefully fix Linux compilation errors
t1m0thyj Feb 7, 2022
a7e7963
Forgot reader must also be mutable
t1m0thyj Feb 7, 2022
59a6077
Fix prompting with named pipes
t1m0thyj Feb 8, 2022
9e46021
Make pipe name consistent on Windows
t1m0thyj Feb 8, 2022
ada5600
Support ZOWE_CLI_HOME variable for daemon socket
t1m0thyj Feb 8, 2022
fbe581a
Test removing complex shutdown logic for macOS
t1m0thyj Feb 8, 2022
9abd0f1
Fix unit tests for sockets and pipes.
awharn Feb 8, 2022
e1cf140
Update the location of the daemon socket on *nix
awharn Feb 8, 2022
0c06766
Revert "Test removing complex shutdown logic for macOS"
awharn Feb 8, 2022
a2d46df
Merge remote-tracking branch 'origin/next' into next-test-unix-socket…
awharn Feb 8, 2022
f740a97
Add changelog entry
awharn Feb 8, 2022
8a97ce4
Add unit tests
awharn Feb 8, 2022
e4a8676
Reset mock after use
awharn Feb 8, 2022
f47c0de
Just mock the return value instead of implementation
awharn Feb 8, 2022
539f7c0
Remove statement causing CI warning
awharn Feb 9, 2022
cfbcc5d
Use NPM ~8.3.2 because of Win CI error with ^8.4.0
awharn Feb 9, 2022
eca24ae
Make requested changes
awharn Feb 9, 2022
15ba33e
Merge remote-tracking branch 'origin/next' into next-test-unix-socket…
awharn Feb 9, 2022
60de50d
Put test under right describe
awharn Feb 9, 2022
25b8e7e
Fix *nix bug, limit concurrency to 1, fix pipeline
awharn Feb 11, 2022
905267a
Fix error due to response code bump
awharn Feb 11, 2022
3c8824d
Merge remote-tracking branch 'origin/next' into next-test-unix-socket…
awharn Feb 15, 2022
4ef3962
Make requested changes
awharn Feb 15, 2022
f0aaad3
Update daemon version.
awharn Feb 17, 2022
13f6250
Update cargo lock
awharn Feb 17, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .github/workflows/zowe-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:

- name: Use NPM v8
id: npm8
run: npm install -g npm@^8
run: npm install -g npm@~8.3.2

- name: Install Node Package Dependencies
id: install
Expand Down Expand Up @@ -129,11 +129,16 @@ jobs:
env:
PATH: ${{ github.workspace }}\__tests__\__resources__\application_instances;${{ env.PATH }}

- name: Integration Tests
id: integration
if: ${{ always() && steps.build.outcome == 'success' }}
- name: Integration Tests (Native)
id: integration-native
if: ${{ always() && steps.build.outcome == 'success' && !(github.event.inputs.test-type == 'binary' || github.event_name == 'push') }}
zFernand0 marked this conversation as resolved.
Show resolved Hide resolved
run: npm run test:integration >> integration-tests.txt

- name: Integration Tests (Daemon)
id: integration-daemon
if: ${{ always() && steps.build.outcome == 'success' && (github.event.inputs.test-type == 'binary' || github.event_name == 'push') }}
zFernand0 marked this conversation as resolved.
Show resolved Hide resolved
run: npm run test:integration -- --runInBand >> integration-tests.txt

- name: Archive Results
id: upload
if: ${{ always() && steps.build.outcome == 'success' }}
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to the Zowe CLI package will be documented in this file.

## Recent Changes

- **NEXT BREAKING** Enhancement: Use sockets and named pipes instead of ports for daemon communication for improved access control.

## `7.0.0-next.202202151759`

- BugFix: Updated Imperative to convert previously used profile property names into V2-compliant property names.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let testEnvironment: ITestEnvironment<ITestPropertiesSchema>;
describe("Zowe native executable", () => {
const exeCantRunDaemonMsg1: string = "You cannot run this 'daemon' command while using the Zowe CLI native executable.";
const exeCantRunDaemonMsg2: string = "Copy and paste the following command instead:";
const EXIT_CODE_CANT_RUN_DAEMON_CMD: number = 107;
const EXIT_CODE_CANT_RUN_DAEMON_CMD: number = 108;

let zoweExePath: string;
let willRunZoweExe: boolean = true;
Expand Down
205 changes: 199 additions & 6 deletions packages/cli/__tests__/daemon/__unit__/DaemonDecider.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@

jest.mock("net");
jest.mock("@zowe/imperative");
import * as fs from "fs";
import * as net from "net";
import * as os from "os";
import * as path from "path";
import Mock = jest.Mock;
import { Imperative } from "@zowe/imperative";
import { DaemonDecider } from "../../../src/daemon/DaemonDecider";
jest.mock("../../../src/daemon//DaemonClient");

describe("DaemonDecider tests", () => {
afterEach(() => {
delete process.env.ZOWE_DAEMON;
});

it("should call normal parse method if no daemon keyword", () => {

Expand Down Expand Up @@ -64,7 +70,7 @@ describe("DaemonDecider tests", () => {
// do nothing
});

const listen = jest.fn((port, hostname, method) => {
const listen = jest.fn((socket, method) => {
// do nothing
method();
});
Expand Down Expand Up @@ -109,7 +115,7 @@ describe("DaemonDecider tests", () => {
expect(err.message).toBe("data");
});

it("should set port based on env variable", () => {
it("should set socket based on env variable", () => {

const log = jest.fn(() => {
// do nothing
Expand All @@ -125,12 +131,199 @@ describe("DaemonDecider tests", () => {

const daemonDecider = new DaemonDecider(["anything"]);

const testPort = "1234";

const testSocket = "/fake/pipe/path";
(daemonDecider as any).mParms = ["one", "two", "--daemon"];
process.env.ZOWE_DAEMON = testPort;
process.env.ZOWE_DAEMON = testSocket;
(daemonDecider as any).initialParse();
expect((daemonDecider as any).mPort).toBe(parseInt(testPort, 10));
expect((daemonDecider as any).mSocket).toBe(process.platform === "win32" ? "\\\\.\\pipe\\" + testSocket : testSocket);

});

it("should not start a daemon", () => {
const log = jest.fn(() => {
// do nothing
});

const on = jest.fn((event, func) => {
// do nothing
});

const listen = jest.fn((event, func) => {
// do nothing
});

const parse = jest.fn( (data, context) => {
expect(data).toBe(undefined);
expect(context).toBe(undefined);
});

(Imperative as any) = {
api: {
appLogger: {
trace: log,
debug: log,
error: log
}
},
console: {
info: log
},
parse
};

const fn = net.createServer as Mock<typeof net.createServer>;
fn.mockImplementation((unusedclient, ...args: any[]) => {
return {on, listen};
});

const daemonDecider = new DaemonDecider(["node", "zowe", "--help"]);
daemonDecider.init();

expect((daemonDecider as any).mSocket).toBeUndefined();
expect((daemonDecider as any).startServer).toBeUndefined();
});

(process.platform === "win32" ? describe : describe.skip)("win32 tests", () => {
it("should use the default socket location (win32)", () => {
const log = jest.fn(() => {
// do nothing
});

const on = jest.fn((event, func) => {
// do nothing
});

const listen = jest.fn((event, func) => {
// do nothing
});

const parse = jest.fn( (data, context) => {
expect(data).toBe(undefined);
expect(context).toBe(undefined);
});

(Imperative as any) = {
api: {
appLogger: {
trace: log,
debug: log,
error: log
}
},
console: {
info: log
},
parse
};

const fn = net.createServer as Mock<typeof net.createServer>;
fn.mockImplementation((unusedclient, ...args: any[]) => {
return {on, listen};
});

const daemonDecider = new DaemonDecider(["node", "zowe", "--daemon"]);
daemonDecider.init();

expect((daemonDecider as any).mSocket).toEqual(`\\\\.\\pipe\\${os.userInfo().username}\\ZoweDaemon`);
});
});

(process.platform !== "win32" ? describe : describe.skip)("non-win32 tests", () => {
it("should use the default socket location (not win32)", () => {
const log = jest.fn(() => {
// do nothing
});

const on = jest.fn((event, func) => {
// do nothing
});

const listen = jest.fn((event, func) => {
// do nothing
});

const parse = jest.fn( (data, context) => {
expect(data).toBe(undefined);
expect(context).toBe(undefined);
});

(Imperative as any) = {
api: {
appLogger: {
trace: log,
debug: log,
error: log
}
},
console: {
info: log
},
parse
};

const fn = net.createServer as Mock<typeof net.createServer>;
fn.mockImplementation((unusedclient, ...args: any[]) => {
return {on, listen};
});

const daemonDecider = new DaemonDecider(["node", "zowe", "--daemon"]);
daemonDecider.init();

expect((daemonDecider as any).mSocket).toEqual(path.join(os.homedir(), ".zowe-daemon.sock"));
});

it("should try to delete an existing socket on *nix", () => {
const log = jest.fn(() => {
// do nothing
});

const on = jest.fn((event, func) => {
// do nothing
});

const listen = jest.fn((event, func) => {
// do nothing
});

const parse = jest.fn( (data, context) => {
expect(data).toBe(undefined);
expect(context).toBe(undefined);
});

(Imperative as any) = {
api: {
appLogger: {
trace: log,
debug: log,
error: log
}
},
console: {
info: log
},
parse
};

const fn = net.createServer as Mock<typeof net.createServer>;
fn.mockImplementation((unusedclient, ...args: any[]) => {
return {on, listen};
});

const daemonDecider = new DaemonDecider(["node", "zowe", "--daemon"]);
const testSocket = "/fake/pipe/path";
process.env.ZOWE_DAEMON = testSocket;

const existsSyncSpy = jest.spyOn(fs, "existsSync");
existsSyncSpy.mockReturnValueOnce(true);

const unlinkSyncSpy = jest.spyOn(fs, "unlinkSync");
unlinkSyncSpy.mockReturnValueOnce(true);

daemonDecider.init();
daemonDecider.runOrUseDaemon();

expect(existsSyncSpy).toHaveBeenCalledTimes(1);
expect(unlinkSyncSpy).toHaveBeenCalledTimes(1);
});
});
});
Loading