Skip to content

Commit

Permalink
refactor: migrate spaces:vpn:wait to oclif/core (#2877)
Browse files Browse the repository at this point in the history
* refactor: initial spaces:vpn:wait command migration

* refactor: complete migration of vpn:wait command

* refactor: fix errors, add json printing, and replace spinner with ux.action

* refactor: migrate tests for spaces:vpn:wait

* remove spaces:vpn:wait command from spaces package

* fix: remove .only
  • Loading branch information
k80bowman authored May 10, 2024
1 parent 30f741a commit 0f9d12c
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 115 deletions.
57 changes: 57 additions & 0 deletions packages/cli/src/commands/spaces/vpn/wait.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import color from '@heroku-cli/color'
import {Command, flags} from '@heroku-cli/command'
import {ux} from '@oclif/core'
import * as Heroku from '@heroku-cli/schema'
import {displayVPNConfigInfo} from '../../../lib/spaces/vpn-connections'

const wait = (ms: number) => new Promise(resolve => {
setTimeout(resolve, ms)
})

export default class Wait extends Command {
static topic = 'spaces';
static description = 'wait for VPN Connection to be created';
static flags = {
space: flags.string({char: 's', description: 'space the vpn connection belongs to', required: true}),
name: flags.string({char: 'n', description: 'name or id of the vpn connection to wait for', required: true}),
json: flags.boolean({description: 'output in json format'}),
interval: flags.string({char: 'i', description: 'seconds to wait between poll intervals'}),
timeout: flags.string({char: 't', description: 'maximum number of seconds to wait'}),
};

public async run(): Promise<void> {
const {flags} = await this.parse(Wait)
const {name, space, json} = flags
const interval = (flags.interval ? Number.parseInt(flags.interval, 10) : 10) * 1000
const timeout = (flags.timeout ? Number.parseInt(flags.timeout, 10) : 20 * 60) * 1000
const deadline = new Date(Date.now() + timeout)
let {body: vpnConnection} = await this.heroku.get<Heroku.PrivateSpacesVpn>(`/spaces/${space}/vpn-connections/${name}`)
if (vpnConnection.status === 'active') {
ux.log('VPN has been allocated.')
return
}

ux.action.start(`Waiting for VPN Connection ${color.green(name)} to allocate...`)
while (vpnConnection.status !== 'active') {
if (new Date() > deadline) {
ux.error('Timeout waiting for VPN to become allocated.', {exit: 1})
}

if (vpnConnection.status === 'failed') {
ux.error(vpnConnection.status_message || '', {exit: 1})
}

await wait(interval)
const {body: updatedVpnConnection} = await this.heroku.get<Heroku.PrivateSpacesVpn>(`/spaces/${space}/vpn-connections/${name}`)
vpnConnection = updatedVpnConnection
}

ux.action.stop()
const {body: newVpnConnection} = await this.heroku.get<Heroku.PrivateSpacesVpn>(`/spaces/${space}/vpn-connections/${name}`)
if (json) {
ux.styledJSON(newVpnConnection)
} else {
displayVPNConfigInfo(space, name, newVpnConnection)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
'use strict'
/* globals beforeEach */

const nock = require('nock')
const cmd = require('../../../../commands/vpn/wait')
const expect = require('chai').expect
const cli = require('@heroku/heroku-cli-util')
import * as nock from 'nock'
import {expect} from 'chai'
import {stderr, stdout} from 'stdout-stderr'
import heredoc from 'tsheredoc'
import {CLIError} from '@oclif/core/lib/errors'
import Cmd from '../../../../../src/commands/spaces/vpn/wait'
import runCommand from '../../../../helpers/runCommand'
import expectOutput from '../../../../helpers/utils/expectOutput'

describe('spaces:vpn:wait', function () {
beforeEach(() => cli.mockConsole())
let api: nock.Scope

it('waits for VPN to allocate and then shows space config', function () {
let api = nock('https://api.heroku.com:443')
.get('/spaces/my-space/vpn-connections/vpn-connection-name-wait')
.reply(200)
afterEach(function () {
api.done()
nock.cleanAll()
})

it('returns an error if the VPN status is updated to failed', async function () {
let errorMessage = ''
api = nock('https://api.heroku.com:443')
.get('/spaces/my-space/vpn-connections/vpn-connection-name-wait')
.reply(200, {
id: '123456789012',
Expand Down Expand Up @@ -50,8 +55,8 @@ describe('spaces:vpn:wait', function () {
routable_cidrs: ['172.16.0.0/16'],
ike_version: 1,
space_cidr_block: '10.0.0.0/16',
status: 'active',
status_message: '',
status: 'failed',
status_message: 'supplied CIDR block already in use',
tunnels: [
{
last_status_change: '2016-10-25T22:09:05Z',
Expand All @@ -71,6 +76,26 @@ describe('spaces:vpn:wait', function () {
},
],
})

try {
await runCommand(Cmd, [
'--space',
'my-space',
'--name',
'vpn-connection-name-wait',
'--interval',
'0',
])
} catch (error: any) {
const {message} = error as CLIError
errorMessage = message
}

expect(errorMessage).to.equal('supplied CIDR block already in use')
})

it('waits for VPN to allocate and then shows space config', async function () {
api = nock('https://api.heroku.com:443')
.get('/spaces/my-space/vpn-connections/vpn-connection-name-wait')
.reply(200, {
id: '123456789012',
Expand All @@ -79,8 +104,8 @@ describe('spaces:vpn:wait', function () {
routable_cidrs: ['172.16.0.0/16'],
ike_version: 1,
space_cidr_block: '10.0.0.0/16',
status: 'active',
status_message: '',
status: 'pending',
status_message: 'supplied CIDR block already in use',
tunnels: [
{
last_status_change: '2016-10-25T22:09:05Z',
Expand All @@ -100,33 +125,15 @@ describe('spaces:vpn:wait', function () {
},
],
})

return cmd.run({flags: {space: 'my-space', name: 'vpn-connection-name-wait', interval: 0}})
.then(() => expect(cli.stderr).to.equal(
'Waiting for VPN Connection vpn-connection-name-wait to allocate... done\n\n'))
.then(() => expect(cli.stdout).to.equal(
`=== vpn-connection-name-wait VPN Tunnels
VPN Tunnel Customer Gateway VPN Gateway Pre-shared Key Routable Subnets IKE Version
────────── ──────────────── ───────────── ────────────── ──────────────── ───────────
Tunnel 1 52.44.146.197 52.44.146.196 apresharedkey1 10.0.0.0/16 1
Tunnel 2 52.44.146.199 52.44.146.198 apresharedkey2 10.0.0.0/16 1\n`,
))
.then(() => api.done())
})

it('returns an error if the VPN status is updated to failed', function () {
let api = nock('https://api.heroku.com:443')
.get('/spaces/my-space/vpn-connections/vpn-connection-name-wait')
.reply(200)
.get('/spaces/my-space/vpn-connections/vpn-connection-name-wait')
.reply(200, {
id: '123456789012',
name: 'vpn-connection-name-config',
name: 'vpn-connection-name-wait',
public_ip: '35.161.69.30',
routable_cidrs: ['172.16.0.0/16'],
ike_version: 1,
space_cidr_block: '10.0.0.0/16',
status: 'pending',
status: 'active',
status_message: '',
tunnels: [
{
Expand Down Expand Up @@ -155,8 +162,8 @@ Tunnel 2 52.44.146.199 52.44.146.198 apresharedkey2 10.0.0.0/16 1
routable_cidrs: ['172.16.0.0/16'],
ike_version: 1,
space_cidr_block: '10.0.0.0/16',
status: 'failed',
status_message: 'supplied CIDR block already in use',
status: 'active',
status_message: '',
tunnels: [
{
last_status_change: '2016-10-25T22:09:05Z',
Expand All @@ -176,16 +183,27 @@ Tunnel 2 52.44.146.199 52.44.146.198 apresharedkey2 10.0.0.0/16 1
},
],
})
await runCommand(Cmd, [
'--space',
'my-space',
'--name',
'vpn-connection-name-wait',
'--interval',
'0',
])

return cmd.run({flags: {space: 'my-space', name: 'vpn-connection-name-wait', interval: 0}})
.catch(error => {
expect(error.message).to.equal('supplied CIDR block already in use')
})
.then(() => api.done())
expect(stderr.output).to.equal('Waiting for VPN Connection vpn-connection-name-wait to allocate......\nWaiting for VPN Connection vpn-connection-name-wait to allocate...... done\n')
expectOutput(stdout.output, heredoc(`
=== vpn-connection-name-wait VPN Tunnels
VPN Tunnel Customer Gateway VPN Gateway Pre-shared Key Routable Subnets IKE Version
────────── ──────────────── ───────────── ────────────── ──────────────── ───────────
Tunnel 1 52.44.146.197 52.44.146.196 apresharedkey1 10.0.0.0/16 1
Tunnel 2 52.44.146.199 52.44.146.198 apresharedkey2 10.0.0.0/16 1
`))
})

it('tells the user if the VPN has been allocated', function () {
let api = nock('https://api.heroku.com:443')
it('tells the user if the VPN has been allocated', async function () {
api = nock('https://api.heroku.com:443')
.get('/spaces/my-space/vpn-connections/vpn-connection-allocated')
.reply(200, {
id: '123456789012',
Expand All @@ -198,9 +216,15 @@ Tunnel 2 52.44.146.199 52.44.146.198 apresharedkey2 10.0.0.0/16 1
status_message: '',
})

return cmd.run({flags: {space: 'my-space', name: 'vpn-connection-allocated', interval: 0}})
.then(() => expect(cli.stdout).to.equal(
'VPN has been allocated.\n'))
.then(() => api.done())
await runCommand(Cmd, [
'--space',
'my-space',
'--name',
'vpn-connection-allocated',
'--interval',
'0',
])
expectOutput(stderr.output, '')
expectOutput(stdout.output, 'VPN has been allocated.\n')
})
})
65 changes: 0 additions & 65 deletions packages/spaces/commands/vpn/wait.js

This file was deleted.

1 change: 0 additions & 1 deletion packages/spaces/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ exports.topics = [

exports.commands = [
require('./commands/vpn/index'),
require('./commands/vpn/wait'),
]

0 comments on commit 0f9d12c

Please sign in to comment.