From aab717c58a50a07545fb6dbeb89ae12f746341f2 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 1 May 2024 14:14:53 -0700 Subject: [PATCH] refactor(spaces): Move command spaces:peering:info to oclif (#2854) * Convert peering info to oclif * Update example to reflect whitespace * Rename to satisfy eslint * Update packages/cli/src/lib/spaces/format.ts Co-authored-by: Justin Wilaby * Fix linting for pg:info test --------- Co-authored-by: Justin Wilaby --- .../cli/src/commands/spaces/peering/info.ts | 62 +++++++++++++++++++ packages/cli/src/lib/spaces/format.ts | 3 + packages/cli/src/lib/spaces/peering.ts | 15 +++++ .../test/unit/commands/pg/info.unit.test.ts | 22 ++++--- .../commands/spaces/peering/info.unit.test.ts | 55 ++++++++++++++++ packages/spaces/commands/peering/info.js | 52 ---------------- packages/spaces/index.js | 1 - .../unit/commands/peering/info.unit.test.js | 47 -------------- 8 files changed, 147 insertions(+), 110 deletions(-) create mode 100644 packages/cli/src/commands/spaces/peering/info.ts create mode 100644 packages/cli/src/lib/spaces/format.ts create mode 100644 packages/cli/src/lib/spaces/peering.ts create mode 100644 packages/cli/test/unit/commands/spaces/peering/info.unit.test.ts delete mode 100644 packages/spaces/commands/peering/info.js delete mode 100644 packages/spaces/test/unit/commands/peering/info.unit.test.js diff --git a/packages/cli/src/commands/spaces/peering/info.ts b/packages/cli/src/commands/spaces/peering/info.ts new file mode 100644 index 0000000000..be86860a9d --- /dev/null +++ b/packages/cli/src/commands/spaces/peering/info.ts @@ -0,0 +1,62 @@ +import {Command, flags} from '@heroku-cli/command' +import {Args, ux} from '@oclif/core' +import * as Heroku from '@heroku-cli/schema' +import heredoc from 'tsheredoc' +import {displayPeeringInfo} from '../../../lib/spaces/peering' + +export default class Info extends Command { + static topic = 'spaces' + static description = heredoc(` + display the information necessary to initiate a peering connection + + You will use the information provided by this command to establish a peering connection request from your AWS VPC to your private space. + + To start the peering process, go into your AWS console for the VPC you would like peered with your Private Space, + navigate to the VPC service, choose the "Peering Connections" option and click the "Create peering connection" button. + + - The AWS Account ID and VPC ID are necessary for the AWS VPC Peering connection wizard. + - You will also need to configure your VPC route table to route the Dyno CIDRs through the peering connection. + + Once you've established the peering connection request, you can use the spaces:peerings:accept command to accept and + configure the peering connection for the space. + `) + + static flags = { + space: flags.string({char: 's', description: 'space to get peering info from'}), + json: flags.boolean({description: 'output in json format'}), + } + + static examples = [heredoc(` + $ heroku spaces:peering:info example-space + === example-space Peering Info + + AWS Account ID: 012345678910 + AWS Region: us-west-2 + AWS VPC ID: vpc-baadf00d + AWS VPC CIDR: 10.0.0.0/16 + Space CIDRs: 10.0.128.0/20, 10.0.144.0/20 + Unavailable CIDRs: 10.1.0.0/16 + `)] + + static args = { + space: Args.string({hidden: true}), + } + + public async run(): Promise { + const {flags, args} = await this.parse(Info) + const spaceName = flags.space || args.space + if (!spaceName) { + ux.error(heredoc(` + Error: Missing 1 required arg: + space + See more help with --help + `)) + } + + const {body: pInfo} = await this.heroku.get(`/spaces/${spaceName}/peering-info`) + if (flags.json) + ux.log(JSON.stringify(pInfo, null, 2)) + else + displayPeeringInfo(spaceName as string, pInfo) + } +} diff --git a/packages/cli/src/lib/spaces/format.ts b/packages/cli/src/lib/spaces/format.ts new file mode 100644 index 0000000000..7873932e88 --- /dev/null +++ b/packages/cli/src/lib/spaces/format.ts @@ -0,0 +1,3 @@ +export function displayCIDR(cidr: string[] | undefined) { + return cidr?.join(', ') ?? '' +} diff --git a/packages/cli/src/lib/spaces/peering.ts b/packages/cli/src/lib/spaces/peering.ts new file mode 100644 index 0000000000..f9484d9d7a --- /dev/null +++ b/packages/cli/src/lib/spaces/peering.ts @@ -0,0 +1,15 @@ +import {ux} from '@oclif/core' +import {displayCIDR} from './format' +import {PeeringInfo} from '@heroku-cli/schema' + +export function displayPeeringInfo(space: string, info: PeeringInfo) { + ux.styledHeader(`${space} Peering Info`) + ux.styledObject({ + 'AWS Account ID': info.aws_account_id, + 'AWS Region': info.aws_region, + 'AWS VPC ID': info.vpc_id, + 'AWS VPC CIDR': info.vpc_cidr, + 'Space CIDRs': displayCIDR(info.space_cidr_blocks), + 'Unavailable CIDRs': displayCIDR(info.unavailable_cidr_blocks), + }, ['AWS Account ID', 'AWS Region', 'AWS VPC ID', 'AWS VPC CIDR', 'Space CIDRs', 'Unavailable CIDRs']) +} diff --git a/packages/cli/test/unit/commands/pg/info.unit.test.ts b/packages/cli/test/unit/commands/pg/info.unit.test.ts index 3db08e8f0d..4ae4ef1895 100644 --- a/packages/cli/test/unit/commands/pg/info.unit.test.ts +++ b/packages/cli/test/unit/commands/pg/info.unit.test.ts @@ -24,22 +24,24 @@ const getCmd = (resultsObject: {allResult: any, addonResult: any}) => { return Cmd } -describe('pg', () => { +describe('pg', function () { let api: nock.Scope let pg: nock.Scope - beforeEach(() => { + + beforeEach(function () { api = nock('https://api.heroku.com:443') pg = nock('https://api.data.heroku.com:443') }) - afterEach(() => { + + afterEach(function () { nock.cleanAll() api.done() pg.done() allSinonStub.restore() addonSinonStub.restore() }) - context('with 0 dbs', () => { - it('shows empty state', async () => { + context('with 0 dbs', function () { + it('shows empty state', async function () { const Cmd = getCmd({allResult: [], addonResult: {}}) api.get('/apps/myapp/config-vars') .reply(200, {}) @@ -51,7 +53,7 @@ describe('pg', () => { expect(stderr.output).to.equal('') }) }) - context('with 2 dbs', () => { + context('with 2 dbs', function () { const plan = {name: 'heroku-postgresql:hobby-dev'} const config = { DATABASE_URL: 'postgres://uxxxxxxxxx:pxxxxxxxx@ec2-54-111-111-1.compute-1.amazonaws.com:5452/dxxxxxxxxxxxx', HEROKU_POSTGRESQL_COBALT_URL: 'postgres://uxxxxxxxxx:pxxxxxxxx@ec2-54-111-111-1.compute-1.amazonaws.com:5452/dxxxxxxxxxxxx', HEROKU_POSTGRESQL_PURPLE_URL: 'postgres://uxxxxxxxxx:pxxxxxxxx@ec3-54-111-111-1.compute-1.amazonaws.com:5452/dxxxxxxxxxxxx', @@ -65,7 +67,7 @@ describe('pg', () => { const dbB = {info: [ {name: 'Plan', values: ['Hobby-dev']}, {name: 'Following', resolve_db_name: true, values: ['postgres://ec2-55-111-111-1.compute-1.amazonaws.com/dxxxxxxxxxxxx']}, ], resource_url: config.HEROKU_POSTGRESQL_PURPLE_URL} - it('shows postgres info', async () => { + it('shows postgres info', async function () { const Cmd = getCmd({allResult: addons, addonResult: {}}) api.get('/apps/myapp/config-vars') .reply(200, config) @@ -80,7 +82,7 @@ describe('pg', () => { expect(stdout.output).to.equal('=== HEROKU_POSTGRESQL_COBALT_URL, DATABASE_URL\n\nPlan: Hobby-dev\nFollowing: HEROKU_POSTGRESQL_COBALT\nBilling App: myapp2\nAdd-on: postgres-1\n\n=== HEROKU_POSTGRESQL_PURPLE_URL\n\nPlan: Hobby-dev\nFollowing: ec2-55-111-111-1.compute-1.amazonaws.com:5432/dxxxxxxxxxxxx\nAdd-on: postgres-2\n\n') expect(stderr.output).to.equal('') }) - it('shows postgres info using attachment names', async () => { + it('shows postgres info using attachment names', async function () { all = [ { id: 1, @@ -104,7 +106,7 @@ describe('pg', () => { ]) expect(stdout.output).to.equal('=== DATABASE_URL, ATTACHMENT_NAME_URL\n\nPlan: Hobby-dev\nFollowing: HEROKU_POSTGRESQL_COBALT\nBilling App: myapp2\nAdd-on: postgres-1\n\n=== HEROKU_POSTGRESQL_PURPLE_URL\n\nPlan: Hobby-dev\nFollowing: ec2-55-111-111-1.compute-1.amazonaws.com:5432/dxxxxxxxxxxxx\nAdd-on: postgres-2\n\n') }) - it('shows postgres info for single database when arg sent in', async () => { + it('shows postgres info for single database when arg sent in', async function () { addon = addons[1] api.get('/apps/myapp/config-vars') .reply(200, config) @@ -120,7 +122,7 @@ describe('pg', () => { expect(stdout.output).to.equal('=== HEROKU_POSTGRESQL_PURPLE_URL\n\nPlan: Hobby-dev\nFollowing: ec2-55-111-111-1.compute-1.amazonaws.com:5432/dxxxxxxxxxxxx\nAdd-on: postgres-2\n\n') expect(stderr.output).to.equal('') }) - it('shows warning for 404', async () => { + it('shows warning for 404', async function () { all = addons api.get('/apps/myapp/config-vars') .reply(200, config) diff --git a/packages/cli/test/unit/commands/spaces/peering/info.unit.test.ts b/packages/cli/test/unit/commands/spaces/peering/info.unit.test.ts new file mode 100644 index 0000000000..5a8c15a189 --- /dev/null +++ b/packages/cli/test/unit/commands/spaces/peering/info.unit.test.ts @@ -0,0 +1,55 @@ +import {stdout} from 'stdout-stderr' +import Cmd from '../../../../../src/commands/spaces/peering/info' +import runCommand from '../../../../helpers/runCommand' +import * as nock from 'nock' +import heredoc from 'tsheredoc' +import expectOutput from '../../../../helpers/utils/expectOutput' +import {expect} from 'chai' +import * as Heroku from '@heroku-cli/schema' + +describe('spaces:peering:info', function () { + let peeringInfo: Heroku.PeeringInfo + + beforeEach(function () { + peeringInfo = { + aws_account_id: '012345678900', + aws_region: 'us-west-2', + vpc_id: 'vpc-1234568a', + vpc_cidr: '10.0.0.0/16', + space_cidr_blocks: ['10.0.128.0/20', '10.0.144.0/20'], + unavailable_cidr_blocks: ['192.168.2.0/30']} + }) + + it('shows space peering info', async function () { + nock('https://api.heroku.com') + .get('/spaces/my-space/peering-info') + .reply(200, peeringInfo) + + await runCommand(Cmd, [ + '--space', + 'my-space', + ]) + expectOutput(stdout.output, heredoc(` + === my-space Peering Info + AWS Account ID: ${peeringInfo.aws_account_id} + AWS Region: ${peeringInfo.aws_region} + AWS VPC ID: ${peeringInfo.vpc_id} + AWS VPC CIDR: ${peeringInfo.vpc_cidr} + Space CIDRs: 10.0.128.0/20, 10.0.144.0/20 + Unavailable CIDRs: 192.168.2.0/30 + `)) + }) + + it('shows peering:info --json', async function () { + nock('https://api.heroku.com') + .get('/spaces/my-space/peering-info') + .reply(200, peeringInfo) + + await runCommand(Cmd, [ + '--space', + 'my-space', + '--json', + ]) + expect(JSON.parse(stdout.output)).to.eql(peeringInfo) + }) +}) diff --git a/packages/spaces/commands/peering/info.js b/packages/spaces/commands/peering/info.js deleted file mode 100644 index c7a051f866..0000000000 --- a/packages/spaces/commands/peering/info.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict' - -let cli = require('heroku-cli-util') - -function displayJSON(info) { - cli.log(JSON.stringify(info, null, 2)) -} - -async function run(context, heroku) { - let lib = require('../../lib/peering')(heroku) - let space = context.flags.space || context.args.space - if (!space) throw new Error('Space name required.\nUSAGE: heroku spaces:peering:info --space my-space') - let pInfo = await lib.getPeeringInfo(space) - if (context.flags.json) displayJSON(pInfo) - else lib.displayPeeringInfo(space, pInfo) -} - -module.exports = { - topic: 'spaces', - command: 'peering:info', - description: 'display the information necessary to initiate a peering connection', - help: `Example: - - $ heroku spaces:peering:info example-space - === example-space Peering Info - AWS Account ID: 012345678910 - AWS Region: us-west-2 - AWS VPC ID: vpc-baadf00d - AWS VPC CIDR: 10.0.0.0/16 - Space CIDRs: 10.0.128.0/20, 10.0.144.0/20 - Unavailable CIDRs: 10.1.0.0/16 - -You will use the information provided by this command to establish a peering connection request from your AWS VPC to your private space. - -To start the peering process, go into your AWS console for the VPC you would like peered with your Private Space, -navigate to the VPC service, choose the "Peering Connections" option and click the "Create peering connection" button. - -- The AWS Account ID and VPC ID are necessary for the AWS VPC Peering connection wizard. -- You will also need to configure your VPC route table to route the Dyno CIDRs through the peering connection. - -Once you've established the peering connection request, you can use the spaces:peerings:accept command to accept and -configure the peering connection for the space. - `, - needsApp: false, - needsAuth: true, - args: [{name: 'space', optional: true, hidden: true}], - flags: [ - {name: 'space', char: 's', hasValue: true, description: 'space to get peering info from'}, - {name: 'json', description: 'output in json format'}, - ], - run: cli.command(run), -} diff --git a/packages/spaces/index.js b/packages/spaces/index.js index 7a22403e82..7dba5977f8 100644 --- a/packages/spaces/index.js +++ b/packages/spaces/index.js @@ -6,7 +6,6 @@ exports.topics = [ ] exports.commands = [ - require('./commands/peering/info'), require('./commands/peering/index'), require('./commands/peering/accept'), require('./commands/peering/destroy'), diff --git a/packages/spaces/test/unit/commands/peering/info.unit.test.js b/packages/spaces/test/unit/commands/peering/info.unit.test.js deleted file mode 100644 index 18bd823201..0000000000 --- a/packages/spaces/test/unit/commands/peering/info.unit.test.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' -/* globals beforeEach */ - -let nock = require('nock') -let cmd = require('../../../../commands/peering/info') -let expect = require('chai').expect -let cli = require('heroku-cli-util') -let info = { - aws_account_id: '012345678900', - aws_region: 'us-west-2', - vpc_id: 'vpc-1234568a', - vpc_cidr: '10.0.0.0/16', - space_cidr_blocks: ['10.0.128.0/20', '10.0.144.0/20'], - unavailable_cidr_blocks: ['192.168.2.0/30']} - -describe('spaces:peering-info', function () { - beforeEach(() => cli.mockConsole()) - - it('shows space peering info', function () { - let api = nock('https://api.heroku.com:443') - .get('/spaces/my-space/peering-info') - .reply(200, - info, - ) - return cmd.run({flags: {space: 'my-space'}}) - .then(() => expect(cli.stdout).to.equal( - `=== my-space Peering Info -AWS Account ID: 012345678900 -AWS Region: us-west-2 -AWS VPC ID: vpc-1234568a -AWS VPC CIDR: 10.0.0.0/16 -Space CIDRs: 10.0.128.0/20, 10.0.144.0/20 -Unavailable CIDRs: 192.168.2.0/30 -`)) - .then(() => api.done()) - }) - - it('shows peering:info --json', function () { - let api = nock('https://api.heroku.com:443') - .get('/spaces/my-space/peering-info') - .reply(200, info) - - return cmd.run({flags: {space: 'my-space', json: true}}) - .then(() => expect(JSON.parse(cli.stdout)).to.eql(info)) - .then(() => api.done()) - }) -})