Skip to content

Commit 621c61b

Browse files
Direct user to firewall rules tab from instance networking view (#3216)
This PR adds a message box — directing the user to the Firewall Rules tab of the VPC — to the instance's Networking tab. <img width="2408" height="2246" alt="image" src="https://github.com/user-attachments/assets/bf12ebd4-a9d6-43d7-a63f-5c7519556291" /> When there are multiple VPCs: <img width="2398" height="2362" alt="image" src="https://github.com/user-attachments/assets/88342f89-3e13-4f23-812e-6d421c07c99d" /> I'm very open to alternate designs and copy. Closes #2064 --------- Co-authored-by: David Crespo <david.crespo@oxidecomputer.com>
1 parent 0abdd33 commit 621c61b

2 files changed

Lines changed: 50 additions & 2 deletions

File tree

app/pages/project/instances/NetworkingTab.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { useQuery } from '@tanstack/react-query'
99
import { createColumnHelper, getCoreRowModel, useReactTable } from '@tanstack/react-table'
1010
import { useCallback, useMemo, useState } from 'react'
1111
import { useForm } from 'react-hook-form'
12-
import { type LoaderFunctionArgs } from 'react-router'
12+
import { Link, type LoaderFunctionArgs } from 'react-router'
1313
import { match } from 'ts-pattern'
1414

1515
import {
@@ -347,6 +347,16 @@ export default function NetworkingTab() {
347347
})
348348
).data.items
349349

350+
// Firewall rules and other external networking state live on the VPC of the
351+
// primary NIC. The parent InstancePage loader prefetches this VPC, but
352+
// primaryNic may be undefined, so we can't use usePrefetchedQuery.
353+
const primaryVpcId = nics.find((nic) => nic.primary)?.vpcId
354+
const { data: primaryVpc } = useQuery({
355+
// primaryVpcId is defined when enabled, so the assertion is safe
356+
...q(api.vpcView, { path: { vpc: primaryVpcId! } }),
357+
enabled: !!primaryVpcId,
358+
})
359+
350360
const { data: siloPools } = usePrefetchedQuery(
351361
q(api.ipPoolList, { query: { limit: ALL_ISH } })
352362
)
@@ -739,7 +749,7 @@ export default function NetworkingTab() {
739749
</CardBlock>
740750

741751
<CardBlock>
742-
<CardBlock.Header title="External Subnets" titleId="attached-subnets-label">
752+
<CardBlock.Header title="External subnets" titleId="attached-subnets-label">
743753
<Button
744754
size="sm"
745755
onClick={() => setAttachSubnetModalOpen(true)}
@@ -777,6 +787,25 @@ export default function NetworkingTab() {
777787
/>
778788
)}
779789
</CardBlock>
790+
<CardBlock>
791+
<CardBlock.Header title="Firewall rules" titleId="firewall-rules-label" />
792+
<CardBlock.Body>
793+
{primaryVpc ? (
794+
<>
795+
Manage firewall rules affecting this instance in VPC{' '}
796+
<Link
797+
className="link-with-underline"
798+
to={pb.vpcFirewallRules({ project, vpc: primaryVpc.name })}
799+
>
800+
{primaryVpc.name}
801+
</Link>
802+
.
803+
</>
804+
) : (
805+
'Firewall rules are managed on the VPC associated with the primary network interface.'
806+
)}
807+
</CardBlock.Body>
808+
</CardBlock>
780809
</div>
781810
)
782811
}

test/e2e/instance-networking.e2e.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,25 @@ const selectASiloImage = async (page: Page, name: string) => {
2222
await page.getByRole('option', { name }).click()
2323
}
2424

25+
test('Instance networking tab — firewall rules card', async ({ page }) => {
26+
// db1 has a primary NIC in mock-vpc, so the card names that VPC
27+
await page.goto('/projects/mock-project/instances/db1/networking')
28+
await expect(
29+
page.getByText('Manage firewall rules affecting this instance in VPC mock-vpc')
30+
).toBeVisible()
31+
32+
// you-fail has no NICs, so there's no primary VPC and we show the fallback copy
33+
await page.goto('/projects/mock-project/instances/you-fail/networking')
34+
await expect(
35+
page.getByText(
36+
'Firewall rules are managed on the VPC associated with the primary network interface.'
37+
)
38+
).toBeVisible()
39+
await expect(
40+
page.getByText('Manage firewall rules affecting this instance in VPC')
41+
).toBeHidden()
42+
})
43+
2544
test('Instance networking tab — NIC table', async ({ page }) => {
2645
await page.goto('/projects/mock-project/instances/db1')
2746

0 commit comments

Comments
 (0)