Skip to content

Commit

Permalink
Add info airnode-deployer command
Browse files Browse the repository at this point in the history
  • Loading branch information
amarthadan committed Sep 27, 2022
1 parent 2b27315 commit 2a78011
Show file tree
Hide file tree
Showing 7 changed files with 386 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/tender-beers-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@api3/airnode-deployer': minor
---

Add `info` airnode-deployer command for retrieving info about the deployment
25 changes: 24 additions & 1 deletion packages/airnode-deployer/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { deploy, removeWithReceipt } from './commands';
import * as logger from '../utils/logger';
import { longArguments } from '../utils/cli';
import { MultiMessageError } from '../utils/infrastructure';
import { listAirnodes, removeAirnode } from '../infrastructure';
import { deploymentInfo, listAirnodes, removeAirnode } from '../infrastructure';

function drawHeader() {
loggerUtils.log(
Expand Down Expand Up @@ -48,6 +48,7 @@ async function runCommand(command: () => Promise<void>) {
const cliExamples = [
'deploy -c config/config.json -s config/secrets.env -r config/receipt.json',
'list --cloud-providers gcp',
'info 5bbcd317',
'remove-with-receipt -r config/receipt.json',
'remove-with-deployment-details --airnode-address 0x6abEdc0A4d1A79eD62160396456c95C5607369D3 --stage dev --cloud-provider aws --region us-east-1',
];
Expand Down Expand Up @@ -193,6 +194,28 @@ yargs(hideBin(process.argv))
await listAirnodes(args.cloudProviders);
}
)
.command(
'info <deployment-id>',
'Displays info about deployed Airnode',
(yargs) => {
yargs.positional('deployment-id', {
description: `ID of the deployment (from 'list' command)`,
type: 'string',
demandOption: true,
});
},
async (args) => {
logger.debugMode(args.debug as boolean);
logger.debug(`Running command ${args._[0]} with arguments ${longArguments(args)}`);

// Looks like due to the bug in yargs (https://github.com/yargs/yargs/issues/1649) we need to specify the type explicitely
const goDeploymentInfo = await go(() => deploymentInfo(args.deploymentId as string));
if (!goDeploymentInfo.success) {
// eslint-disable-next-line functional/immutable-data
process.exitCode = 1;
}
}
)
.example(cliExamples.map((line) => [`$0 ${line}\n`]))
.help()
.demandCommand(1)
Expand Down
169 changes: 168 additions & 1 deletion packages/airnode-deployer/src/infrastructure/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { getSpinner } from '../utils/logger';
import { parseSecretsFile } from '../utils';
import { Directory, DirectoryStructure } from '../utils/infrastructure';
import { mockBucketDirectoryStructure } from '../../test/fixtures';
import { listAirnodes01, listAirnodes02, listAirnodes03 } from '../../test/snapshots';
import { deploymentInfo01, listAirnodes01, listAirnodes02, listAirnodes03 } from '../../test/snapshots';

const exec = jest.fn();
jest.spyOn(util, 'promisify').mockImplementation(() => exec);
Expand Down Expand Up @@ -992,3 +992,170 @@ describe('listAirnodes', () => {
expect(gcpGetFileFromBucketSpy).not.toHaveBeenCalled();
});
});

describe('deploymentInfo', () => {
const bucket = 'airnode-123456789';
const configPath = path.join(__dirname, '..', '..', 'test', 'fixtures', 'config.valid.json');
const directoryStructure = pick(mockBucketDirectoryStructure, [
'0xd0624E6C2C8A1DaEdE9Fa7E9C409167ed5F256c6',
'0xA30CA71Ba54E83127214D3271aEA8F5D6bD4Dace',
]);

let awsGetAirnodeBucketSpy: jest.SpyInstance;
let awsGetBucketDirectoryStructureSpy: jest.SpyInstance;
let awsGetFileFromBucketSpy: jest.SpyInstance;
let gcpGetAirnodeBucketSpy: jest.SpyInstance;
let gcpGetBucketDirectoryStructureSpy: jest.SpyInstance;
let gcpGetFileFromBucketSpy: jest.SpyInstance;
let consoleSpy: jest.SpyInstance;

beforeEach(() => {
awsGetAirnodeBucketSpy = jest.spyOn(aws, 'getAirnodeBucket').mockResolvedValue(bucket);
awsGetBucketDirectoryStructureSpy = jest
.spyOn(aws, 'getBucketDirectoryStructure')
.mockResolvedValue(directoryStructure);
awsGetFileFromBucketSpy = jest
.spyOn(aws, 'getFileFromBucket')
.mockResolvedValue(fs.readFileSync(configPath).toString());
gcpGetAirnodeBucketSpy = jest.spyOn(gcp, 'getAirnodeBucket').mockResolvedValue(bucket);
gcpGetBucketDirectoryStructureSpy = jest
.spyOn(gcp, 'getBucketDirectoryStructure')
.mockResolvedValue(directoryStructure);
gcpGetFileFromBucketSpy = jest
.spyOn(gcp, 'getFileFromBucket')
.mockResolvedValue(fs.readFileSync(configPath).toString());
consoleSpy = jest.spyOn(console, 'log');
});

it('shows info about the deployment', async () => {
consoleSpy.mockImplementationOnce(() => {});
consoleSpy.mockImplementationOnce(() => {});
consoleSpy.mockImplementationOnce(() => {});
consoleSpy.mockImplementationOnce(() => {});
consoleSpy.mockImplementationOnce(() => {});
consoleSpy.mockImplementationOnce((output: string) => {
for (const line of deploymentInfo01.split('\n')) {
expect(output).toContain(line);
if (line.includes('3580a278')) {
// eslint-disable-next-line jest/no-conditional-expect
expect(output).toContain('(current)');
}
}
});

const deploymentId = '7195b548';

const originalColorVariable = process.env.FORCE_COLOR;
// I have to disable table coloring so I can compare the output
process.env.FORCE_COLOR = '0';
await infrastructure.deploymentInfo(deploymentId);
process.env.FORCE_COLOR = originalColorVariable;

expect(awsGetAirnodeBucketSpy).toHaveBeenCalledTimes(1);
expect(gcpGetAirnodeBucketSpy).not.toHaveBeenCalled();
expect(awsGetBucketDirectoryStructureSpy).toHaveBeenCalledTimes(1);
expect(awsGetBucketDirectoryStructureSpy).toHaveBeenCalledWith(bucket);
expect(gcpGetBucketDirectoryStructureSpy).not.toHaveBeenCalled();
expect(awsGetFileFromBucketSpy).toHaveBeenCalledTimes(1);
expect(awsGetFileFromBucketSpy).toHaveBeenNthCalledWith(
1,
bucket,
'0xd0624E6C2C8A1DaEdE9Fa7E9C409167ed5F256c6/dev/1662558010204/config.json'
);
expect(gcpGetFileFromBucketSpy).not.toHaveBeenCalled();
expect(consoleSpy).toHaveBeenNthCalledWith(1, 'Cloud provider: AWS (us-east-1)');
expect(consoleSpy).toHaveBeenNthCalledWith(2, 'Airnode address: 0xd0624E6C2C8A1DaEdE9Fa7E9C409167ed5F256c6');
expect(consoleSpy).toHaveBeenNthCalledWith(3, 'Stage: dev');
expect(consoleSpy).toHaveBeenNthCalledWith(4, 'Airnode version: 0.8.0');
expect(consoleSpy).toHaveBeenNthCalledWith(5, 'Deployment ID: 7195b548');
});

it(`fails if there's a problem with the cloud provider`, async () => {
const expectedError = new Error('example error');
awsGetAirnodeBucketSpy = jest.spyOn(aws, 'getAirnodeBucket').mockRejectedValue(expectedError);

const deploymentId = '7195b548';

const originalColorVariable = process.env.FORCE_COLOR;
// I have to disable table coloring so I can compare the output
process.env.FORCE_COLOR = '0';
await expect(infrastructure.deploymentInfo(deploymentId)).rejects.toThrow(
new Error(`No deployment with id '${deploymentId}' found`)
);
process.env.FORCE_COLOR = originalColorVariable;

expect(awsGetAirnodeBucketSpy).toHaveBeenCalledTimes(1);
expect(gcpGetAirnodeBucketSpy).toHaveBeenCalledTimes(1);
expect(awsGetBucketDirectoryStructureSpy).not.toHaveBeenCalled();
expect(gcpGetBucketDirectoryStructureSpy).toHaveBeenCalledTimes(1);
expect(gcpGetBucketDirectoryStructureSpy).toHaveBeenCalledWith(bucket);
expect(awsGetFileFromBucketSpy).not.toHaveBeenCalled();
expect(gcpGetFileFromBucketSpy).toHaveBeenCalledTimes(3);
expect(gcpGetFileFromBucketSpy).toHaveBeenNthCalledWith(
1,
bucket,
'0xd0624E6C2C8A1DaEdE9Fa7E9C409167ed5F256c6/dev/1662558010204/config.json'
);
expect(gcpGetFileFromBucketSpy).toHaveBeenNthCalledWith(
2,
bucket,
'0xd0624E6C2C8A1DaEdE9Fa7E9C409167ed5F256c6/prod/1662558071950/config.json'
);
expect(gcpGetFileFromBucketSpy).toHaveBeenNthCalledWith(
3,
bucket,
'0xA30CA71Ba54E83127214D3271aEA8F5D6bD4Dace/dev/1662559204554/config.json'
);
});

it(`fails if the deployment can't be found`, async () => {
const nonexistingDeploymentId = '2c6ef2b3';

const originalColorVariable = process.env.FORCE_COLOR;
// I have to disable table coloring so I can compare the output
process.env.FORCE_COLOR = '0';
await expect(infrastructure.deploymentInfo(nonexistingDeploymentId)).rejects.toThrow(
new Error(`No deployment with id '${nonexistingDeploymentId}' found`)
);
process.env.FORCE_COLOR = originalColorVariable;

expect(awsGetAirnodeBucketSpy).toHaveBeenCalledTimes(1);
expect(gcpGetAirnodeBucketSpy).toHaveBeenCalledTimes(1);
expect(awsGetBucketDirectoryStructureSpy).toHaveBeenCalledTimes(1);
expect(awsGetBucketDirectoryStructureSpy).toHaveBeenCalledWith(bucket);
expect(gcpGetBucketDirectoryStructureSpy).toHaveBeenCalledTimes(1);
expect(gcpGetBucketDirectoryStructureSpy).toHaveBeenCalledWith(bucket);
expect(awsGetFileFromBucketSpy).toHaveBeenCalledTimes(3);
expect(awsGetFileFromBucketSpy).toHaveBeenNthCalledWith(
1,
bucket,
'0xd0624E6C2C8A1DaEdE9Fa7E9C409167ed5F256c6/dev/1662558010204/config.json'
);
expect(awsGetFileFromBucketSpy).toHaveBeenNthCalledWith(
2,
bucket,
'0xd0624E6C2C8A1DaEdE9Fa7E9C409167ed5F256c6/prod/1662558071950/config.json'
);
expect(awsGetFileFromBucketSpy).toHaveBeenNthCalledWith(
3,
bucket,
'0xA30CA71Ba54E83127214D3271aEA8F5D6bD4Dace/dev/1662559204554/config.json'
);
expect(gcpGetFileFromBucketSpy).toHaveBeenCalledTimes(3);
expect(gcpGetFileFromBucketSpy).toHaveBeenNthCalledWith(
1,
bucket,
'0xd0624E6C2C8A1DaEdE9Fa7E9C409167ed5F256c6/dev/1662558010204/config.json'
);
expect(gcpGetFileFromBucketSpy).toHaveBeenNthCalledWith(
2,
bucket,
'0xd0624E6C2C8A1DaEdE9Fa7E9C409167ed5F256c6/prod/1662558071950/config.json'
);
expect(gcpGetFileFromBucketSpy).toHaveBeenNthCalledWith(
3,
bucket,
'0xA30CA71Ba54E83127214D3271aEA8F5D6bD4Dace/dev/1662559204554/config.json'
);
});
});
Loading

0 comments on commit 2a78011

Please sign in to comment.