Skip to content

Commit 07b6c15

Browse files
authored
tooling: make mock API default firewall rules more accurate (#2349)
* remove allow-rdp from default firewall rules * populate VPC with default rules on creation in mock API, update e2e test * do the clone test even better * missed one
1 parent d32fddc commit 07b6c15

File tree

7 files changed

+158
-115
lines changed

7 files changed

+158
-115
lines changed

mock-api/msw/handlers.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import { GiB } from '~/util/units'
2727
import { genCumulativeI64Data } from '../metrics'
2828
import { serial } from '../serial'
2929
import { defaultSilo, toIdp } from '../silo'
30+
import { getTimestamps } from '../util'
31+
import { defaultFirewallRules } from '../vpc'
3032
import {
3133
db,
3234
getIpFromPool,
@@ -41,7 +43,6 @@ import {
4143
errIfInvalidDiskSize,
4244
forbiddenErr,
4345
getStartAndEndTime,
44-
getTimestamps,
4546
handleMetrics,
4647
ipInAnyRange,
4748
ipRangeLen,
@@ -997,6 +998,9 @@ export const handlers = makeHandlers({
997998
}
998999
db.vpcSubnets.push(newSubnet)
9991000

1001+
// populate default firewall rules
1002+
db.vpcFirewallRules.push(...defaultFirewallRules(newVpc.id))
1003+
10001004
return json(newVpc, { status: 201 })
10011005
},
10021006
vpcView: ({ path, query }) => lookup.vpc({ ...path, ...query }),

mock-api/msw/util.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@ export function getStartAndEndTime(params: { startTime?: Date; endTime?: Date })
8686
return { startTime, endTime }
8787
}
8888

89-
export function getTimestamps() {
90-
const now = new Date().toISOString()
91-
return { time_created: now, time_modified: now }
92-
}
93-
9489
export const forbiddenErr = () =>
9590
json({ error_code: 'Forbidden', request_id: 'fake-id' }, { status: 403 })
9691

mock-api/util.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
5+
*
6+
* Copyright Oxide Computer Company
7+
*/
8+
9+
// this is only in its own file so it can be used in both the mock resources and
10+
// the mock handlers without circular import issues
11+
12+
export function getTimestamps() {
13+
const now = new Date().toISOString()
14+
return { time_created: now, time_modified: now }
15+
}

mock-api/vpc.ts

Lines changed: 59 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
* Copyright Oxide Computer Company
77
*/
88

9+
import { v4 as uuid } from 'uuid'
10+
911
import type { Vpc, VpcFirewallRule, VpcSubnet } from '@oxide/api'
1012

1113
import type { Json } from './json-type'
1214
import { project, project2 } from './project'
15+
import { getTimestamps } from './util'
1316

1417
const time_created = new Date(2021, 0, 1).toISOString()
1518
const time_modified = new Date(2021, 0, 2).toISOString()
@@ -63,78 +66,67 @@ export const vpcSubnet2: Json<VpcSubnet> = {
6366
ipv4_block: '10.1.1.2/24',
6467
}
6568

66-
export const firewallRules: Json<VpcFirewallRule[]> = [
67-
{
68-
id: 'b74aeea8-1201-4efd-b6ec-011f10a0b176',
69-
name: 'allow-internal-inbound',
70-
status: 'enabled',
71-
direction: 'inbound',
72-
targets: [{ type: 'vpc', value: 'default' }],
73-
action: 'allow',
74-
description:
75-
'allow inbound traffic to all instances within the VPC if originated within the VPC',
76-
filters: {
77-
hosts: [{ type: 'vpc', value: 'default' }],
69+
export function defaultFirewallRules(vpcId: string): Json<VpcFirewallRule[]> {
70+
return [
71+
{
72+
id: uuid(),
73+
vpc_id: vpcId,
74+
name: 'allow-internal-inbound',
75+
status: 'enabled',
76+
direction: 'inbound',
77+
targets: [{ type: 'vpc', value: 'default' }],
78+
action: 'allow',
79+
description:
80+
'allow inbound traffic to all instances within the VPC if originated within the VPC',
81+
filters: {
82+
hosts: [{ type: 'vpc', value: 'default' }],
83+
},
84+
priority: 65534,
85+
...getTimestamps(),
7886
},
79-
priority: 65534,
80-
time_created,
81-
time_modified,
82-
vpc_id: vpc.id,
83-
},
84-
{
85-
id: '9802cd8e-1e59-4fdf-9b40-99c189f7a19b',
86-
name: 'allow-ssh',
87-
status: 'enabled',
88-
direction: 'inbound',
89-
targets: [{ type: 'vpc', value: 'default' }],
90-
description: 'allow inbound TCP connections on port 22 from anywhere',
91-
filters: {
92-
ports: ['22'],
93-
protocols: ['TCP'],
87+
{
88+
id: uuid(),
89+
vpc_id: vpcId,
90+
name: 'allow-ssh',
91+
status: 'enabled',
92+
direction: 'inbound',
93+
targets: [{ type: 'vpc', value: 'default' }],
94+
description: 'allow inbound TCP connections on port 22 from anywhere',
95+
filters: {
96+
ports: ['22'],
97+
protocols: ['TCP'],
98+
},
99+
action: 'allow',
100+
priority: 65534,
101+
...getTimestamps(),
94102
},
95-
action: 'allow',
96-
priority: 65534,
97-
time_created,
98-
time_modified,
99-
vpc_id: vpc.id,
100-
},
101-
{
102-
id: 'cde07d86-b8c0-49ed-8754-55f1bdee20fe',
103-
name: 'allow-icmp',
104-
status: 'enabled',
105-
direction: 'inbound',
106-
targets: [{ type: 'vpc', value: 'default' }],
107-
description: 'allow inbound ICMP traffic from anywhere',
108-
filters: {
109-
protocols: ['ICMP'],
110-
},
111-
action: 'allow',
112-
priority: 65534,
113-
time_created,
114-
time_modified,
115-
vpc_id: vpc.id,
116-
},
117-
{
118-
id: '5ed562d9-2566-496d-b7b3-7976b04a0b80',
119-
name: 'allow-rdp',
120-
status: 'enabled',
121-
direction: 'inbound',
122-
targets: [{ type: 'vpc', value: 'default' }],
123-
description: 'allow inbound TCP connections on port 3389 from anywhere',
124-
filters: {
125-
ports: ['3389'],
126-
protocols: ['TCP'],
103+
{
104+
id: uuid(),
105+
vpc_id: vpcId,
106+
name: 'allow-icmp',
107+
status: 'enabled',
108+
direction: 'inbound',
109+
targets: [{ type: 'vpc', value: 'default' }],
110+
description: 'allow inbound ICMP traffic from anywhere',
111+
filters: {
112+
protocols: ['ICMP'],
113+
},
114+
action: 'allow',
115+
priority: 65534,
116+
...getTimestamps(),
127117
},
128-
action: 'allow',
129-
priority: 65534,
130-
time_created,
131-
time_modified,
132-
vpc_id: vpc.id,
133-
},
118+
]
119+
}
120+
121+
// usually we try to hard-code resource IDs, but in this case
122+
// we don't rely on them anywhere and it's easier to wrap up if they're dynamic
123+
124+
export const firewallRules: Json<VpcFirewallRule[]> = [
125+
...defaultFirewallRules(vpc.id),
134126
// second mock VPC in other project, meant to test display with lots of
135127
// targets and filters
136128
{
137-
id: '097c849e-68c8-43f7-9ceb-b1855c51f178',
129+
id: uuid(),
138130
name: 'lots-of-filters',
139131
status: 'enabled',
140132
direction: 'inbound',
@@ -156,7 +148,7 @@ export const firewallRules: Json<VpcFirewallRule[]> = [
156148
vpc_id: vpc2.id,
157149
},
158150
{
159-
id: '097c849e-68c8-43f7-9ceb-b1855c51f178',
151+
id: uuid(),
160152
name: 'lots-of-targets',
161153
status: 'enabled',
162154
direction: 'inbound',

test/e2e/firewall-rules.e2e.ts

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import { clickRowAction, expect, expectRowVisible, test } from './utils'
1010

11-
const defaultRules = ['allow-internal-inbound', 'allow-ssh', 'allow-icmp', 'allow-rdp']
11+
const defaultRules = ['allow-internal-inbound', 'allow-ssh', 'allow-icmp']
1212

1313
test('can create firewall rule', async ({ page }) => {
1414
await page.goto('/projects/mock-project/vpcs/mock-vpc')
@@ -19,7 +19,7 @@ test('can create firewall rule', async ({ page }) => {
1919
await expect(page.locator(`text="${name}"`)).toBeVisible()
2020
}
2121
const rows = page.locator('tbody >> tr')
22-
await expect(rows).toHaveCount(4)
22+
await expect(rows).toHaveCount(3)
2323

2424
const modal = page.getByRole('dialog', { name: 'Add firewall rule' })
2525
await expect(modal).toBeHidden()
@@ -100,7 +100,7 @@ test('can create firewall rule', async ({ page }) => {
100100
const tooltip = page.getByRole('tooltip', { name: 'Other filters UDP Port 123-' })
101101
await expect(tooltip).toBeVisible()
102102

103-
await expect(rows).toHaveCount(5)
103+
await expect(rows).toHaveCount(4)
104104
for (const name of defaultRules) {
105105
await expect(page.locator(`text="${name}"`)).toBeVisible()
106106
}
@@ -241,7 +241,7 @@ test('can update firewall rule', async ({ page }) => {
241241
await page.getByRole('tab', { name: 'Firewall Rules' }).click()
242242

243243
const rows = page.locator('tbody >> tr')
244-
await expect(rows).toHaveCount(4)
244+
await expect(rows).toHaveCount(3)
245245

246246
// allow-icmp is the one we're doing to change
247247
const oldNameCell = page.locator('td >> text="allow-icmp"')
@@ -298,7 +298,7 @@ test('can update firewall rule', async ({ page }) => {
298298
await expect(newNameCell).toBeVisible()
299299
await expect(oldNameCell).toBeHidden()
300300

301-
await expect(rows).toHaveCount(4)
301+
await expect(rows).toHaveCount(3)
302302

303303
// new target shows up in target cell
304304
await expect(page.locator('text=subnetedit-filter-subnetICMP')).toBeVisible()
@@ -317,28 +317,44 @@ test('create from existing rule', async ({ page }) => {
317317
const modal = page.getByRole('dialog', { name: 'Add firewall rule' })
318318
await expect(modal).toBeHidden()
319319

320-
await clickRowAction(page, 'allow-rdp', 'Clone')
320+
await clickRowAction(page, 'allow-icmp', 'Clone')
321+
322+
await expect(page).toHaveURL(url + '-new/allow-icmp')
323+
await expect(modal).toBeVisible()
324+
await expect(modal.getByRole('textbox', { name: 'Name', exact: true })).toHaveValue(
325+
'allow-icmp-copy'
326+
)
327+
328+
await expect(modal.getByRole('checkbox', { name: 'TCP' })).not.toBeChecked()
329+
await expect(modal.getByRole('checkbox', { name: 'UDP' })).not.toBeChecked()
330+
await expect(modal.getByRole('checkbox', { name: 'ICMP' })).toBeChecked()
331+
332+
// no port filters
333+
const portFilters = modal.getByRole('table', { name: 'Port filters' })
334+
await expect(portFilters).toBeHidden()
335+
336+
const targets = modal.getByRole('table', { name: 'Targets' })
337+
await expect(targets.getByRole('row', { name: 'Name: default, Type: vpc' })).toBeVisible()
338+
339+
// close the modal
340+
await page.keyboard.press('Escape')
341+
await expect(modal).toBeHidden()
342+
343+
// do it again with a different rule
344+
await clickRowAction(page, 'allow-ssh', 'Clone')
321345

322-
await expect(page).toHaveURL(url + '-new/allow-rdp')
323346
await expect(modal).toBeVisible()
324347
await expect(modal.getByRole('textbox', { name: 'Name', exact: true })).toHaveValue(
325-
'allow-rdp-copy'
348+
'allow-ssh-copy'
326349
)
327350

351+
await expect(portFilters.getByRole('cell', { name: '22', exact: true })).toBeVisible()
352+
328353
await expect(modal.getByRole('checkbox', { name: 'TCP' })).toBeChecked()
329354
await expect(modal.getByRole('checkbox', { name: 'UDP' })).not.toBeChecked()
330355
await expect(modal.getByRole('checkbox', { name: 'ICMP' })).not.toBeChecked()
331356

332-
await expect(
333-
modal
334-
.getByRole('table', { name: 'Port filters' })
335-
.getByRole('cell', { name: '3389', exact: true })
336-
).toBeVisible()
337-
await expect(
338-
modal
339-
.getByRole('table', { name: 'Targets' })
340-
.getByRole('row', { name: 'Name: default, Type: vpc' })
341-
).toBeVisible()
357+
await expect(targets.getByRole('row', { name: 'Name: default, Type: vpc' })).toBeVisible()
342358
})
343359

344360
const rulePath = '/projects/mock-project/vpcs/mock-vpc/firewall-rules/allow-icmp/edit'

0 commit comments

Comments
 (0)