-
Notifications
You must be signed in to change notification settings - Fork 227
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'prerelease/9.0.0-alpha' into sbosio/spaces-drains-get
- Loading branch information
Showing
14 changed files
with
410 additions
and
446 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import {Command, flags} from '@heroku-cli/command' | ||
import {Args, ux} from '@oclif/core' | ||
import * as Heroku from '@heroku-cli/schema' | ||
import heredoc from 'tsheredoc' | ||
|
||
export default class Index extends Command { | ||
static aliases = ['trusted-ips'] | ||
static topic = 'trusted-ips' | ||
static description = heredoc(` | ||
list trusted IP ranges for a space | ||
Trusted IP ranges are only available on Private Spaces. | ||
The space name is a required parameter. Newly created spaces will have 0.0.0.0/0 set by default | ||
allowing all traffic to applications in the space. More than one CIDR block can be provided at | ||
a time to the commands listed below. For example 1.2.3.4/20 and 5.6.7.8/20 can be added with: | ||
`) | ||
|
||
static flags = { | ||
space: flags.string({char: 's', description: 'space to get inbound rules from'}), | ||
json: flags.boolean({description: 'output in json format'}), | ||
} | ||
|
||
static args = { | ||
space: Args.string({hidden: true}), | ||
} | ||
|
||
public async run(): Promise<void> { | ||
const {flags, args} = await this.parse(Index) | ||
const space = flags.space || args.space | ||
if (!space) { | ||
throw new Error('Space name required.\nUSAGE: heroku trusted-ips my-space') | ||
} | ||
|
||
const {body: rules} = await this.heroku.get<Required<Heroku.InboundRuleset>>(`/spaces/${space}/inbound-ruleset`, | ||
{ | ||
headers: {Accept: 'application/vnd.heroku+json; version=3.dogwood'}, | ||
}) | ||
|
||
if (flags.json) { | ||
ux.log(JSON.stringify(rules, null, 2)) | ||
} else { | ||
this.displayRules(space, rules) | ||
} | ||
} | ||
|
||
private displayRules(space: string, ruleset: Required<Heroku.InboundRuleset>) { | ||
if (ruleset.rules.length > 0) { | ||
ux.styledHeader('Trusted IP Ranges') | ||
for (const rule of ruleset.rules) { | ||
ux.log(rule.source) | ||
} | ||
} else { | ||
ux.styledHeader(`${space} has no trusted IP ranges. All inbound web requests to dynos are blocked.`) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import color from '@heroku-cli/color' | ||
import {Command, flags} from '@heroku-cli/command' | ||
import {Args, ux} from '@oclif/core' | ||
import * as Heroku from '@heroku-cli/schema' | ||
import heredoc from 'tsheredoc' | ||
|
||
export default class Remove extends Command { | ||
static aliases = ['trusted-ips:remove'] | ||
static topic = 'trusted-ips' | ||
static description = heredoc(` | ||
Remove a range from the list of trusted IP ranges | ||
Uses CIDR notation.`) | ||
|
||
static examples = [heredoc(` | ||
$ heroku trusted-ips:remove --space my-space 192.168.2.0/24 | ||
Removed 192.168.2.0/24 from trusted IP ranges on my-space | ||
`)] | ||
|
||
static flags = { | ||
space: flags.string({optional: false, description: 'space to remove rule from'}), | ||
confirm: flags.string({description: 'set to space name to bypass confirm prompt'}), | ||
} | ||
|
||
static args = { | ||
source: Args.string({required: true}), | ||
} | ||
|
||
public async run(): Promise<void> { | ||
const {flags, args} = await this.parse(Remove) | ||
const space = flags.space | ||
const url = `/spaces/${space}/inbound-ruleset` | ||
const opts = {headers: {Accept: 'application/vnd.heroku+json; version=3.dogwood'}} | ||
const {body: rules} = await this.heroku.get<Heroku.InboundRuleset>(url, opts) | ||
if (rules.rules?.length === 0) { | ||
throw new Error('No IP ranges are configured. Nothing to do.') | ||
} | ||
|
||
const originalLength = rules.rules?.length | ||
rules.rules = rules.rules?.filter(r => r.source !== args.source) | ||
if (rules.rules?.length === originalLength) { | ||
throw new Error(`No IP range matching ${args.source} was found.`) | ||
} | ||
|
||
await this.heroku.put(url, {...opts, body: rules}) | ||
ux.log(`Removed ${color.cyan.bold(args.source)} from trusted IP ranges on ${color.cyan.bold(space)}`) | ||
ux.warn('It may take a few moments for the changes to take effect.') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
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 {displayCIDR, displayVPNStatus} from '../../../lib/spaces/format' | ||
|
||
export default class Info extends Command { | ||
static topic = 'spaces'; | ||
static description = 'display the information for VPN'; | ||
static example = heredoc(` | ||
$ heroku spaces:vpn:info --space my-space vpn-connection-name | ||
=== vpn-connection-name VPN Tunnel Info | ||
Name: vpn-connection-name | ||
ID: 123456789012 | ||
Public IP: 35.161.69.30 | ||
Routable CIDRs: 172.16.0.0/16 | ||
Status: failed | ||
Status Message: supplied CIDR block already in use | ||
=== my-space Tunnel Info | ||
VPN Tunnel IP Address Status Last Changed Details | ||
────────── ───────────── ────── ──────────────────── ───────────── | ||
Tunnel 1 52.44.146.197 UP 2016-10-25T22:09:05Z status message | ||
Tunnel 2 52.44.146.197 UP 2016-10-25T22:09:05Z status message | ||
`) | ||
|
||
static flags = { | ||
space: flags.string({ | ||
char: 's', | ||
description: 'space the vpn connection belongs to', | ||
required: true, | ||
}), | ||
json: flags.boolean({description: 'output in json format'}), | ||
} | ||
|
||
static args = { | ||
name: Args.string({ | ||
description: 'name or id of the VPN connection to get info from', | ||
required: true, | ||
}), | ||
} | ||
|
||
public async run(): Promise<void> { | ||
const {flags, args} = await this.parse(Info) | ||
const {space, json} = flags | ||
const {name} = args | ||
const {body: vpnConnection} = await this.heroku.get<Heroku.PrivateSpacesVpn>(`/spaces/${space}/vpn-connections/${name}`) | ||
const connectionName = vpnConnection.name || name | ||
this.render(connectionName, vpnConnection, json) | ||
} | ||
|
||
private displayVPNInfo(name: string, vpnConnection: Heroku.PrivateSpacesVpn) { | ||
ux.styledHeader(`${name} VPN Info`) | ||
ux.styledObject({ | ||
Name: name, | ||
ID: vpnConnection.id, | ||
'Public IP': vpnConnection.public_ip, | ||
'Routable CIDRs': displayCIDR(vpnConnection.routable_cidrs), | ||
Status: `${displayVPNStatus(vpnConnection.status)}`, | ||
'Status Message': vpnConnection.status_message, | ||
}, ['Name', 'ID', 'Public IP', 'Routable CIDRs', 'State', 'Status', 'Status Message']) | ||
const vpnConnectionTunnels = vpnConnection.tunnels || [] | ||
vpnConnectionTunnels.forEach((val, i) => { | ||
val.tunnel_id = 'Tunnel ' + (i + 1) | ||
}) | ||
ux.styledHeader(`${name} VPN Tunnel Info`) | ||
ux.table(vpnConnectionTunnels, { | ||
tunnel_id: {header: 'VPN Tunnel'}, | ||
ip: {header: 'IP Address'}, | ||
status: { | ||
header: 'Status', | ||
get: row => displayVPNStatus(row.status), | ||
}, | ||
last_status_change: {header: 'Status Last Changed'}, | ||
status_message: {header: 'Details'}, | ||
}) | ||
} | ||
|
||
private render(name: string, vpnConnection: Heroku.PrivateSpacesVpn, json: boolean) { | ||
if (json) { | ||
ux.styledJSON(vpnConnection) | ||
} else { | ||
this.displayVPNInfo(name, vpnConnection) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
packages/cli/test/unit/commands/spaces/trusted-ips/index.unit.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import {expect} from '@oclif/test' | ||
import * as nock from 'nock' | ||
import {stdout} from 'stdout-stderr' | ||
import heredoc from 'tsheredoc' | ||
import Cmd from '../../../../../src/commands/spaces/trusted-ips' | ||
import runCommand from '../../../../helpers/runCommand' | ||
|
||
const now = new Date() | ||
|
||
describe('trusted-ips', function () { | ||
it('shows the trusted IP ranges', async function () { | ||
const api = nock('https://api.heroku.com:443') | ||
.get('/spaces/my-space/inbound-ruleset') | ||
.reply(200, { | ||
version: '1', | ||
default_action: 'allow', | ||
created_at: now, | ||
created_by: 'dickeyxxx', | ||
rules: [ | ||
{source: '127.0.0.1/20', action: 'allow'}, | ||
], | ||
}) | ||
await runCommand(Cmd, ['--space', 'my-space']) | ||
expect(stdout.output).to.equal(heredoc(` | ||
=== Trusted IP Ranges | ||
127.0.0.1/20 | ||
`)) | ||
api.done() | ||
}) | ||
|
||
it('shows the trusted IP ranges with blank rules', async function () { | ||
const api = nock('https://api.heroku.com:443') | ||
.get('/spaces/my-space/inbound-ruleset') | ||
.reply(200, { | ||
version: '1', | ||
default_action: 'allow', | ||
created_at: now, | ||
created_by: 'dickeyxxx', | ||
rules: [], | ||
}) | ||
await runCommand(Cmd, ['--space', 'my-space']) | ||
expect(stdout.output).to.equal('=== my-space has no trusted IP ranges. All inbound web requests to dynos are blocked.\n\n') | ||
api.done() | ||
}) | ||
|
||
it('shows the trusted IP ranges --json', async function () { | ||
const ruleSet = { | ||
version: '1', | ||
default_action: 'allow', | ||
created_at: now.toISOString(), | ||
created_by: 'dickeyxxx', | ||
rules: [ | ||
{source: '127.0.0.1/20', action: 'allow'}, | ||
], | ||
} | ||
|
||
const api = nock('https://api.heroku.com:443') | ||
.get('/spaces/my-space/inbound-ruleset') | ||
.reply(200, ruleSet) | ||
await runCommand(Cmd, ['--space', 'my-space', '--json', 'true']) | ||
expect(JSON.parse(stdout.output)).to.eql(ruleSet) | ||
api.done() | ||
}) | ||
}) |
33 changes: 33 additions & 0 deletions
33
packages/cli/test/unit/commands/spaces/trusted-ips/remove.unit.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import {expect} from '@oclif/test' | ||
import * as nock from 'nock' | ||
import {stdout} from 'stdout-stderr' | ||
import heredoc from 'tsheredoc' | ||
import Cmd from '../../../../../src/commands/spaces/trusted-ips/remove' | ||
import runCommand from '../../../../helpers/runCommand' | ||
|
||
describe('trusted-ips:remove', function () { | ||
it('removes a CIDR entry from the trusted IP ranges', async function () { | ||
const api = nock('https://api.heroku.com:443') | ||
.get('/spaces/my-space/inbound-ruleset') | ||
.reply(200, { | ||
created_by: 'dickeyxxx', | ||
rules: [ | ||
{source: '128.0.0.1/20', action: 'allow'}, | ||
{source: '127.0.0.1/20', action: 'allow'}, | ||
], | ||
}, | ||
) | ||
.put('/spaces/my-space/inbound-ruleset', { | ||
created_by: 'dickeyxxx', | ||
rules: [ | ||
{source: '128.0.0.1/20', action: 'allow'}, | ||
], | ||
}) | ||
.reply(200, {rules: []}) | ||
await runCommand(Cmd, ['127.0.0.1/20', '--space', 'my-space']) | ||
expect(stdout.output).to.eq(heredoc(` | ||
Removed 127.0.0.1/20 from trusted IP ranges on my-space | ||
`)) | ||
api.done() | ||
}) | ||
}) |
Oops, something went wrong.