From ff2827ce32f36f70c5af165837718a7a98da5275 Mon Sep 17 00:00:00 2001 From: Roger Qiu Date: Mon, 7 Feb 2022 20:19:39 +1100 Subject: [PATCH] WIP --- src/bin/errors.ts | 27 +++------------- src/bin/nodes/CommandFind.ts | 63 +++++++++++++++++++++++------------- src/bin/nodes/CommandPing.ts | 50 +++++++++++++++++++--------- src/bin/utils/options.ts | 15 +++++++++ tests/bin/nodes/find.test.ts | 4 +++ tests/bin/nodes/ping.test.ts | 4 +++ 6 files changed, 102 insertions(+), 61 deletions(-) diff --git a/src/bin/errors.ts b/src/bin/errors.ts index 05cf5eff2..4e31848f5 100644 --- a/src/bin/errors.ts +++ b/src/bin/errors.ts @@ -51,28 +51,12 @@ class ErrorCLIFileRead extends ErrorCLI { exitCode = sysexits.NOINPUT; } -class ErrorSecretPathFormat extends ErrorCLI { - description = "Secret name needs to be of format: ':'"; - exitCode = 64; -} - -class ErrorVaultNameAmbiguous extends ErrorCLI { - description = - 'There is more than 1 Vault with this name. Please specify a Vault ID'; - exitCode = 1; -} - -class ErrorSecretsUndefined extends ErrorCLI { - description = 'At least one secret must be specified as an argument'; - exitCode = 64; -} - -class ErrorNodeFindFailed extends ErrorCLI { +class ErrorCLINodeFindFailed extends ErrorCLI { description = 'Failed to find the node in the DHT'; exitCode = 1; } -class ErrorNodePingFailed extends ErrorCLI { +class ErrorCLINodePingFailed extends ErrorCLI { description = 'Node was not online or not found.'; exitCode = 1; } @@ -88,9 +72,6 @@ export { ErrorCLIPasswordFileRead, ErrorCLIRecoveryCodeFileRead, ErrorCLIFileRead, - ErrorSecretPathFormat, - ErrorVaultNameAmbiguous, - ErrorSecretsUndefined, - ErrorNodeFindFailed, - ErrorNodePingFailed, + ErrorCLINodeFindFailed, + ErrorCLINodePingFailed, }; diff --git a/src/bin/nodes/CommandFind.ts b/src/bin/nodes/CommandFind.ts index 09150b5dd..40cc87ec3 100644 --- a/src/bin/nodes/CommandFind.ts +++ b/src/bin/nodes/CommandFind.ts @@ -7,6 +7,7 @@ import * as binOptions from '../utils/options'; import * as binProcessors from '../utils/processors'; import * as binParsers from '../utils/parsers'; import * as binErrors from '../errors'; +import { sleep } from '../../utils'; class CommandFind extends CommandPolykey { constructor(...args: ConstructorParameters) { @@ -17,6 +18,8 @@ class CommandFind extends CommandPolykey { this.addOption(binOptions.nodeId); this.addOption(binOptions.clientHost); this.addOption(binOptions.clientPort); + this.addOption(binOptions.retryCount); + this.addOption(binOptions.retryInterval); this.action(async (nodeId: NodeId, options) => { const { default: PolykeyClient } = await import('../../PolykeyClient'); const nodesPB = await import('../../proto/js/polykey/v1/nodes/nodes_pb'); @@ -57,28 +60,42 @@ class CommandFind extends CommandPolykey { host: '', port: 0, }; - try { - const response = await binUtils.retryAuthentication( - (auth) => pkClient.grpcClient.nodesFind(nodeMessage, auth), - meta, - ); - result.success = true; - result.id = response.getNodeId(); - result.host = response.getAddress()!.getHost(); - result.port = response.getAddress()!.getPort(); - result.message = `Found node at ${networkUtils.buildAddress( - result.host as Host, - result.port as Port, - )}`; - } catch (err) { - if (!(err instanceof nodesErrors.ErrorNodeGraphNodeNotFound)) - throw err; - // Else failed to find the node. - result.success = false; - result.id = nodesUtils.encodeNodeId(nodeId); - result.host = ''; - result.port = 0; - result.message = `Failed to find node ${result.id}`; + let attemptCount = 1; + const maxAttempts: number | undefined = options.retryCount; + const attemptDelay: number = options.retryInterval; + while (true) { + try { + const response = await binUtils.retryAuthentication( + (auth) => pkClient.grpcClient.nodesFind(nodeMessage, auth), + meta, + ); + result.success = true; + result.id = response.getNodeId(); + result.host = response.getAddress()!.getHost(); + result.port = response.getAddress()!.getPort(); + result.message = `Found node at ${networkUtils.buildAddress( + result.host as Host, + result.port as Port, + )}`; + break; + } catch (err) { + if (!(err instanceof nodesErrors.ErrorNodeGraphNodeNotFound)) { + throw err; + } + if (maxAttempts !== undefined && attemptCount < maxAttempts) { + // Delay and continue. + attemptCount++; + await sleep(attemptDelay); + continue; + } + // Else failed to find the node. + result.success = false; + result.id = nodesUtils.encodeNodeId(nodeId); + result.host = ''; + result.port = 0; + result.message = `Failed to find node ${result.id}`; + break; + } } let output: any = result; if (options.format === 'human') output = [result.message]; @@ -90,7 +107,7 @@ class CommandFind extends CommandPolykey { ); // Like ping it should error when failing to find node for automation reasons. if (!result.success) - throw new binErrors.ErrorNodeFindFailed(result.message); + throw new binErrors.ErrorCLINodeFindFailed(result.message); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/bin/nodes/CommandPing.ts b/src/bin/nodes/CommandPing.ts index 997ddaeb7..f17a0135d 100644 --- a/src/bin/nodes/CommandPing.ts +++ b/src/bin/nodes/CommandPing.ts @@ -6,6 +6,7 @@ import * as binOptions from '../utils/options'; import * as binProcessors from '../utils/processors'; import * as binParsers from '../utils/parsers'; import * as binErrors from '../errors'; +import { sleep } from '../../utils'; class CommandPing extends CommandPolykey { constructor(...args: ConstructorParameters) { @@ -16,6 +17,8 @@ class CommandPing extends CommandPolykey { this.addOption(binOptions.nodeId); this.addOption(binOptions.clientHost); this.addOption(binOptions.clientPort); + this.addOption(binOptions.retryCount); + this.addOption(binOptions.retryInterval); this.action(async (nodeId: NodeId, options) => { const { default: PolykeyClient } = await import('../../PolykeyClient'); const nodesUtils = await import('../../nodes/utils'); @@ -49,26 +52,43 @@ class CommandPing extends CommandPolykey { nodeMessage.setNodeId(nodesUtils.encodeNodeId(nodeId)); let statusMessage; let error; - try { - statusMessage = await binUtils.retryAuthentication( - (auth) => pkClient.grpcClient.nodesPing(nodeMessage, auth), - meta, - ); - } catch (err) { - if (err instanceof nodesErrors.ErrorNodeGraphNodeNotFound) { - error = new binErrors.ErrorNodePingFailed( - `Failed to resolve node ID ${nodesUtils.encodeNodeId( - nodeId, - )} to an address.`, + let attemptCount = 1; + const maxAttempts: number | undefined = options.retryCount; + const attemptDelay: number = options.retryInterval; + while (true) { + try { + statusMessage = await binUtils.retryAuthentication( + (auth) => pkClient.grpcClient.nodesPing(nodeMessage, auth), + meta, ); - } else { - throw err; + if (statusMessage.getSuccess()) break; + if (maxAttempts !== undefined && attemptCount < maxAttempts) { + // Delay and continue + attemptCount++; + await sleep(attemptDelay); + continue; + } + break; + } catch (err) { + if (err instanceof nodesErrors.ErrorNodeGraphNodeNotFound) + throw err; + if (maxAttempts !== undefined && attemptCount < maxAttempts) { + // Delay and continue + attemptCount++; + await sleep(attemptDelay); + continue; + } + error = new binErrors.ErrorCLINodePingFailed( + `Failed to resolve node ID ${nodesUtils.encodeNodeId(nodeId)} to an address`, + ); + break; } } const status = { success: false, message: '' }; status.success = statusMessage ? statusMessage.getSuccess() : false; - if (!status.success && !error) - error = new binErrors.ErrorNodePingFailed('No response received'); + if (!status.success && !error) { + error = new binErrors.ErrorCLINodePingFailed('No response received'); + } if (status.success) status.message = 'Node is Active.'; else status.message = error.message; const output: any = diff --git a/src/bin/utils/options.ts b/src/bin/utils/options.ts index e7832b67c..11bb66b48 100644 --- a/src/bin/utils/options.ts +++ b/src/bin/utils/options.ts @@ -155,6 +155,19 @@ const workers = new commander.Option( .argParser(binParsers.parseCoreCount) .default(undefined); + +const retryCount = new commander.Option( + '-rc --retry-count ', + 'Number of attempts before failing', +).argParser(binParsers.parseNumber); + +const retryInterval = new commander.Option( + '-ri --retry-interval ', + 'Number of milliseconds between each attempt', +) + .argParser(binParsers.parseNumber) + .default(5000); + export { nodePath, format, @@ -176,4 +189,6 @@ export { seedNodes, network, workers, + retryCount, + retryInterval, }; diff --git a/tests/bin/nodes/find.test.ts b/tests/bin/nodes/find.test.ts index cb9a09bed..0f80eec8e 100644 --- a/tests/bin/nodes/find.test.ts +++ b/tests/bin/nodes/find.test.ts @@ -181,6 +181,10 @@ describe('find', () => { const commands = genCommands([ 'find', nodesUtils.encodeNodeId(unknownNodeId), + '-c', + '3', + '-ad', + '100', ]); const result = await testBinUtils.pkStdio(commands, {}, dataDir); expect(result.exitCode).toBe(1); diff --git a/tests/bin/nodes/ping.test.ts b/tests/bin/nodes/ping.test.ts index c86424fa9..e5e52a791 100644 --- a/tests/bin/nodes/ping.test.ts +++ b/tests/bin/nodes/ping.test.ts @@ -101,6 +101,10 @@ describe('ping', () => { const commands = genCommands([ 'ping', nodesUtils.encodeNodeId(remoteOfflineNodeId), + '-c', + '1', + '-ad', + '100', ]); const result = await testBinUtils.pkStdio(commands, {}, dataDir); expect(result.exitCode).toBe(1); // Should fail with no response. for automation purposes.