diff --git a/packages/cli/src/commands/spaces/destroy.ts b/packages/cli/src/commands/spaces/destroy.ts index f5fd959cf0..52f5a24922 100644 --- a/packages/cli/src/commands/spaces/destroy.ts +++ b/packages/cli/src/commands/spaces/destroy.ts @@ -1,12 +1,13 @@ import {Args, ux} from '@oclif/core' -import color from '@heroku-cli/color' import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import heredoc from 'tsheredoc' import confirmCommand from '../../lib/confirmCommand' import {displayNat} from '../../lib/spaces/spaces' +import color from '@heroku-cli/color' +import {Space} from '../../lib/types/fir' -type RequiredSpaceWithNat = Required & {outbound_ips?: Required} +type RequiredSpaceWithNat = Required & {outbound_ips?: Required} export default class Destroy extends Command { static topic = 'spaces' @@ -40,10 +41,24 @@ export default class Destroy extends Command { let natWarning = '' const {body: space} = await this.heroku.get(`/spaces/${spaceName}`) + if (space.state === 'allocated') { ({body: space.outbound_ips} = await this.heroku.get>(`/spaces/${spaceName}/nat`)) if (space.outbound_ips && space.outbound_ips.state === 'enabled') { - natWarning = `The Outbound IPs for this space will be reused!\nEnsure that external services no longer allow these Outbound IPs: ${displayNat(space.outbound_ips)}\n` + const ipv6 = space.generation?.name === 'fir' ? ' and IPv6' : '' + natWarning = heredoc` + ${color.dim('===')} ${color.bold('WARNING: Outbound IPs Will Be Reused')} + ${color.yellow(`⚠️ Deleting this space frees up the following outbound IPv4${ipv6} IPs for reuse:`)} + ${color.bold(displayNat(space.outbound_ips) ?? '')} + + ${color.dim('Update the following configurations:')} + ${color.dim('=')} IP allowlists + ${color.dim('=')} Firewall rules + ${color.dim('=')} Security group configurations + ${color.dim('=')} Network ACLs + + ${color.yellow(`Ensure that you remove the listed IPv4${ipv6} addresses from your security configurations.`)} + ` } } diff --git a/packages/cli/test/unit/commands/spaces/destroy.unit.test.ts b/packages/cli/test/unit/commands/spaces/destroy.unit.test.ts index 2bea983a55..f0156dcb76 100644 --- a/packages/cli/test/unit/commands/spaces/destroy.unit.test.ts +++ b/packages/cli/test/unit/commands/spaces/destroy.unit.test.ts @@ -4,30 +4,101 @@ import runCommand from '../../../helpers/runCommand' import * as nock from 'nock' import {expect} from 'chai' import heredoc from 'tsheredoc' +import {ux} from '@oclif/core' +import * as sinon from 'sinon' describe('spaces:destroy', function () { const now = new Date() + beforeEach(function () { + sinon.stub(ux, 'prompt').resolves('my-space') + }) + afterEach(function () { nock.cleanAll() + sinon.restore() }) - it('destroys a space', async function () { + it('shows extended NAT warning for fir generation space', async function () { const api = nock('https://api.heroku.com') .get('/spaces/my-space') - .reply(200, {name: 'my-space', team: {name: 'my-team'}, region: {name: 'my-region'}, state: 'allocated', created_at: now}) + .reply(200, { + name: 'my-space', + team: {name: 'my-team'}, + region: {name: 'my-region'}, + state: 'allocated', + created_at: now, + generation: {name: 'fir'}, + }) .get('/spaces/my-space/nat') .reply(200, {state: 'enabled', sources: ['1.1.1.1', '2.2.2.2']}) .delete('/spaces/my-space') .reply(200) - await runCommand(Cmd, ['--space', 'my-space', '--confirm', 'my-space']) + await runCommand(Cmd, ['--space', 'my-space']) + api.done() + const replacer = /([»›])/g + expect(stderr.output.replace(replacer, '')).to.eq(heredoc(` › Warning: Destructive Action + › This command will destroy the space my-space + › === WARNING: Outbound IPs Will Be Reused + › ⚠️ Deleting this space frees up the following outbound IPv4 and IPv6 IPs + › for reuse: + › 1.1.1.1, 2.2.2.2 + › + › Update the following configurations: + › = IP allowlists + › = Firewall rules + › = Security group configurations + › = Network ACLs + › + › Ensure that you remove the listed IPv4 and IPv6 addresses from your + › security configurations. + › + › + + Destroying space my-space... + Destroying space my-space... done + `.replace(replacer, ''))) + }) + + it('shows simple NAT warning for non-fir generation space', async function () { + const api = nock('https://api.heroku.com') + .get('/spaces/my-space') + .reply(200, { + name: 'my-space', + team: {name: 'my-team'}, + region: {name: 'my-region'}, + state: 'allocated', + created_at: now, + generation: {name: 'cedar'}, + }) + .get('/spaces/my-space/nat') + .reply(200, {state: 'enabled', sources: ['1.1.1.1', '2.2.2.2']}) + .delete('/spaces/my-space') + .reply(200) + await runCommand(Cmd, ['--space', 'my-space']) api.done() + const replacer = /([»›])/g + expect(stderr.output.replace(replacer, '')).to.eq(heredoc(` › Warning: Destructive Action + › This command will destroy the space my-space + › === WARNING: Outbound IPs Will Be Reused + › ⚠️ Deleting this space frees up the following outbound IPv4 IPs for reuse: + › 1.1.1.1, 2.2.2.2 + › + › Update the following configurations: + › = IP allowlists + › = Firewall rules + › = Security group configurations + › = Network ACLs + › + › Ensure that you remove the listed IPv4 addresses from your security + › configurations. + › + › - expect(stderr.output).to.eq(heredoc` - Destroying space my-space... - Destroying space my-space... done - `) + Destroying space my-space... + Destroying space my-space... done + `.replace(replacer, ''))) }) })