Skip to content

Commit

Permalink
The processClientStatus and processClientOptions only reads statu…
Browse files Browse the repository at this point in the history
…s if none of the client options are set

The client options of `--node-id`, `--client-host`, `--client-port` are required connection parameters
When partially set, the values are no longer defaulted to the values in status
  • Loading branch information
CMCDragonkai committed Mar 10, 2022
1 parent 8ef8b28 commit 2df87bb
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 82 deletions.
4 changes: 3 additions & 1 deletion src/bin/agent/CommandStop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class CommandStop extends CommandPolykey {
this.logger.info('Agent is already stopping');
return;
} else if (statusInfo?.status === 'STARTING') {
throw new binErrors.ErrorCLIStatusStarting();
throw new binErrors.ErrorCLIPolykeyAgentStatus(
'agent is starting'
);
}
const meta = await binProcessors.processAuthentication(
options.passwordFile,
Expand Down
39 changes: 16 additions & 23 deletions src/bin/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,15 @@ class ErrorCLINodePath extends ErrorCLI {
exitCode = sysexits.USAGE;
}

class ErrorCLIStatusMissing extends ErrorCLI {
class ErrorCLIClientOptions extends ErrorCLI {
description =
'Could not resolve nodeId, clientHost or clientPort from non-existent Status';
'Missing required client options';
exitCode = sysexits.USAGE;
}

class ErrorCLIStatusNotLive extends ErrorCLI {
description =
'Could not resolve nodeId, clientHost or clientPort from Status';
exitCode = sysexits.USAGE;
}

class ErrorCLIStatusStarting extends ErrorCLI {
description = 'Agent is starting';
exitCode = sysexits.TEMPFAIL;
}

class ErrorCLIPolykeyAgentProcess extends ErrorCLI {
description = 'PolykeyAgent process could not be started';
exitCode = sysexits.OSERR;
}

class ErrorCLIPasswordMissing extends ErrorCLI {
description =
'Password is necessary, provide it via PK_PASSWORD, --password-file or when prompted';
'Password is necessary, provide it via --password-file, PK_PASSWORD or when prompted';
exitCode = sysexits.USAGE;
}

Expand All @@ -51,6 +35,16 @@ class ErrorCLIFileRead extends ErrorCLI {
exitCode = sysexits.NOINPUT;
}

class ErrorCLIPolykeyAgentStatus extends ErrorCLI {
description = 'PolykeyAgent agent status';
exitCode = sysexits.TEMPFAIL;
}

class ErrorCLIPolykeyAgentProcess extends ErrorCLI {
description = 'PolykeyAgent process could not be started';
exitCode = sysexits.OSERR;
}

class ErrorNodeFindFailed extends ErrorCLI {
description = 'Failed to find the node in the DHT';
exitCode = 1;
Expand All @@ -64,14 +58,13 @@ class ErrorNodePingFailed extends ErrorCLI {
export {
ErrorCLI,
ErrorCLINodePath,
ErrorCLIClientOptions,
ErrorCLIPasswordMissing,
ErrorCLIStatusMissing,
ErrorCLIStatusStarting,
ErrorCLIStatusNotLive,
ErrorCLIPolykeyAgentProcess,
ErrorCLIPasswordFileRead,
ErrorCLIRecoveryCodeFileRead,
ErrorCLIFileRead,
ErrorCLIPolykeyAgentStatus,
ErrorCLIPolykeyAgentProcess,
ErrorNodeFindFailed,
ErrorNodePingFailed,
};
138 changes: 90 additions & 48 deletions src/bin/utils/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import path from 'path';
import prompts from 'prompts';
import * as grpc from '@grpc/grpc-js';
import Logger from '@matrixai/logger';
import * as binErrors from '../errors';
import Status from '../../status/Status';
import * as clientUtils from '../../client/utils';
import { Status } from '../../status';
import * as binErrors from '../errors';
import { arrayZip } from '../../utils';
import config from '../../config';

/**
Expand Down Expand Up @@ -185,7 +186,7 @@ async function processRecoveryCode(
* 1. Reads --node-id, --client-host, --client-port
* 2. Reads PK_NODE_ID, PK_CLIENT_HOST, PK_CLIENT_PORT
* 3. Command-specific defaults
* 4. Reads Status
* 4. If no options are set, reads Status
* Step 2 is done during option construction
* Step 3 is done in CommandPolykey classes
*/
Expand All @@ -201,7 +202,13 @@ async function processClientOptions(
clientHost: Host;
clientPort: Port;
}> {
if (nodeId == null || clientHost == null || clientPort == null) {
if (nodeId != null && clientHost != null && clientPort != null) {
return {
nodeId,
clientHost,
clientPort,
};
} else if (nodeId == null && clientHost == null && clientPort == null) {
const statusPath = path.join(nodePath, config.defaults.statusBase);
const statusLockPath = path.join(nodePath, config.defaults.statusLockBase);
const status = new Status({
Expand All @@ -212,24 +219,39 @@ async function processClientOptions(
});
const statusInfo = await status.readStatus();
if (statusInfo === undefined || statusInfo.status !== 'LIVE') {
throw new binErrors.ErrorCLIStatusNotLive();
throw new binErrors.ErrorCLIPolykeyAgentStatus(
'agent is not live'
);
}
if (nodeId == null) nodeId = statusInfo.data.nodeId;
if (clientHost == null) clientHost = statusInfo.data.clientHost;
if (clientPort == null) clientPort = statusInfo.data.clientPort;
return {
nodeId: statusInfo.data.nodeId,
clientHost: statusInfo.data.clientHost,
clientPort: statusInfo.data.clientPort,
};
} else {
const errorMsg = arrayZip(
[nodeId, clientHost, clientPort],
[
'missing node ID, provide it with --node-id or PK_NODE_ID',
'missing client host, provide it with --client-host or PK_CLIENT_HOST',
'missing client port, provide it with --client-port or PK_CLIENT_PORT'
]
).flatMap(([option, msg]) => {
if (option == null) {
return [msg];
} else {
return [];
}
}).join('; ');
throw new binErrors.ErrorCLIClientOptions(errorMsg);
}
return {
nodeId,
clientHost,
clientPort,
};
}

/**
* Process client status
* Options are used for connecting PolykeyClient
* Variant of processClientOptions
* Use this when you need always need the status info
* Use this when you need always need the status info when reading the status
*/
async function processClientStatus(
nodePath: string,
Expand Down Expand Up @@ -261,7 +283,6 @@ async function processClientStatus(
clientPort: Port;
}
> {
// If all parameters are set, no status and no statusInfo is used
if (nodeId != null && clientHost != null && clientPort != null) {
return {
statusInfo: undefined,
Expand All @@ -270,40 +291,61 @@ async function processClientStatus(
clientHost,
clientPort,
};
} else if (nodeId == null && clientHost == null && clientPort == null) {
const statusPath = path.join(nodePath, config.defaults.statusBase);
const statusLockPath = path.join(nodePath, config.defaults.statusLockBase);
const status = new Status({
statusPath,
statusLockPath,
fs,
logger: logger.getChild(Status.name),
});
const statusInfo = await status.readStatus();
if (statusInfo == null) {
return {
statusInfo: { status: 'DEAD', data: {} },
status,
nodeId: undefined,
clientHost: undefined,
clientPort: undefined,
};
} else if (statusInfo.status === 'LIVE') {
nodeId = statusInfo.data.nodeId;
clientHost = statusInfo.data.clientHost;
clientPort = statusInfo.data.clientPort;
return {
statusInfo,
status,
nodeId,
clientHost,
clientPort,
};
} else {
return {
statusInfo,
status,
nodeId: undefined,
clientHost: undefined,
clientPort: undefined,
};
}
} else {
const errorMsg = arrayZip(
[nodeId, clientHost, clientPort],
[
'missing node ID, provide it with --node-id or PK_NODE_ID',
'missing client host, provide it with --client-host or PK_CLIENT_HOST',
'missing client port, provide it with --client-port or PK_CLIENT_PORT'
]
).flatMap(([option, msg]) => {
if (option == null) {
return [msg];
} else {
return [];
}
}).join('; ');
throw new binErrors.ErrorCLIClientOptions(errorMsg);
}
const statusPath = path.join(nodePath, config.defaults.statusBase);
const statusLockPath = path.join(nodePath, config.defaults.statusLockBase);
const status = new Status({
statusPath,
statusLockPath,
fs,
logger: logger.getChild(Status.name),
});
const statusInfo = await status.readStatus();
// If not all parameters are set, and that the status doesn't exist
// Then this an exception
if (statusInfo == null) {
throw new binErrors.ErrorCLIStatusMissing();
}
if (statusInfo.status === 'LIVE') {
if (nodeId == null) nodeId = statusInfo.data.nodeId;
if (clientHost == null) clientHost = statusInfo.data.clientHost;
if (clientPort == null) clientPort = statusInfo.data.clientPort;
return {
statusInfo,
status,
nodeId,
clientHost,
clientPort,
};
}
return {
statusInfo,
status,
nodeId,
clientHost,
clientPort,
};
}

/**
Expand Down
16 changes: 7 additions & 9 deletions tests/bin/agent/status.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import path from 'path';
import fs from 'fs';
import Logger, { LogLevel, StreamHandler } from '@matrixai/logger';
import Status from '@/status/Status';
import config from '@/config';
import * as nodesUtils from '@/nodes/utils';
import * as binErrors from '@/bin/errors';
import config from '@/config';
import * as testBinUtils from '../utils';
import * as testUtils from '../../utils';

Expand Down Expand Up @@ -113,17 +112,16 @@ describe('status', () => {
global.defaultTimeout * 2,
);
test('status on missing agent', async () => {
const { exitCode, stderr } = await testBinUtils.pkStdio(
['agent', 'status', '--verbose'],
const { exitCode, stdout } = await testBinUtils.pkStdio(
['agent', 'status', '--format', 'json'],
{
PK_NODE_PATH: path.join(dataDir, 'polykey'),
},
);
testBinUtils.expectProcessError(
exitCode,
stderr,
new binErrors.ErrorCLIStatusMissing(),
);
expect(exitCode).toBe(0);
expect(JSON.parse(stdout)).toMatchObject({
status: 'DEAD',
});
});
describe('status with global agent', () => {
let globalAgentDir;
Expand Down
2 changes: 1 addition & 1 deletion tests/bin/agent/stop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ describe('stop', () => {
testBinUtils.expectProcessError(
exitCode,
stderr,
new binErrors.ErrorCLIStatusStarting(),
new binErrors.ErrorCLIPolykeyAgentStatus('agent is starting'),
);
await status.waitFor('LIVE');
await testBinUtils.pkStdio(
Expand Down

0 comments on commit 2df87bb

Please sign in to comment.