Skip to content

Commit

Permalink
Add snapshot action to instance disks list (#1774)
Browse files Browse the repository at this point in the history
add snapshot action to instance disks
  • Loading branch information
david-crespo authored Oct 6, 2023
1 parent 0f01550 commit b2b3a74
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 3 deletions.
28 changes: 27 additions & 1 deletion app/pages/project/instances/instance/tabs/StorageTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import type { LoaderFunctionArgs } from 'react-router-dom'
import {
type Disk,
apiQueryClient,
diskCan,
genName,
instanceCan,
useApiMutation,
useApiQueryClient,
Expand Down Expand Up @@ -60,11 +62,35 @@ export function StorageTab() {
)

const detachDisk = useApiMutation('instanceDiskDetach', {})
const createSnapshot = useApiMutation('snapshotCreate', {
onSuccess() {
queryClient.invalidateQueries('snapshotList')
addToast({ content: 'Snapshot successfully created' })
},
})

const { data: instance } = usePrefetchedApiQuery('instanceView', instancePathQuery)

const makeActions = useCallback(
(disk: Disk): MenuAction[] => [
{
label: 'Snapshot',
disabled: !diskCan.snapshot(disk) && (
<>
Only disks in state {fancifyStates(diskCan.snapshot.states)} can be snapshotted
</>
),
onActivate() {
createSnapshot.mutate({
query: { project },
body: {
name: genName(disk.name),
disk: disk.name,
description: '',
},
})
},
},
{
label: 'Detach',
disabled: !instanceCan.detachDisk(instance) && (
Expand All @@ -82,7 +108,7 @@ export function StorageTab() {
},
},
],
[detachDisk, instance, queryClient, instancePathQuery]
[detachDisk, instance, queryClient, instancePathQuery, createSnapshot, project]
)

const attachDisk = useApiMutation('instanceDiskAttach', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@
*
* Copyright Oxide Computer Company
*/
import { expect, expectNotVisible, expectVisible, stopInstance, test } from '../utils'
import {
clickRowAction,
expect,
expectNotVisible,
expectRowVisible,
expectVisible,
stopInstance,
test,
} from '../utils'

test('Attach disk', async ({ page }) => {
await page.goto('/projects/mock-project/instances/db1')
Expand Down Expand Up @@ -57,3 +65,23 @@ test('Attach disk', async ({ page }) => {
await page.click('role=button[name="Attach Disk"]')
await expectVisible(page, ['role=cell[name="disk-3"]'])
})

test('Snapshot disk', async ({ page }) => {
await page.goto('/projects/mock-project/instances/db1')

// have to use nth with toasts because the text shows up in multiple spots
const successMsg = page.getByText('Snapshot successfully created').nth(0)
await expect(successMsg).toBeHidden()

await clickRowAction(page, 'disk-2', 'Snapshot')

await expect(successMsg).toBeVisible() // we see the toast!

// now go see the snapshot on the snapshots page
await page.getByRole('link', { name: 'Snapshots' }).click()
const table = page.getByRole('table')
await expectRowVisible(table, {
name: expect.stringMatching(/^disk-2-/),
disk: 'disk-2',
})
})
8 changes: 7 additions & 1 deletion app/test/e2e/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ export async function expectNotVisible(page: Page, selectors: Selector[]) {
}
}

// Technically this has type AsymmetricMatcher, which is not exported by
// Playwright and is (surprisingly) just Record<string, any>. Rather than use
// that, I think it's smarter to do the following in case they ever make the
// type more interesting; this will still do what it's supposed to.
type StringMatcher = ReturnType<typeof expect.stringMatching>

/**
* Assert that a row matching `expectedRow` is present in `table`. The match
* uses `objectContaining`, so `expectedRow` does not need to contain every
Expand All @@ -55,7 +61,7 @@ export async function expectNotVisible(page: Page, selectors: Selector[]) {
*/
export async function expectRowVisible(
table: Locator,
expectedRow: Record<string, string>
expectedRow: Record<string, string | StringMatcher>
) {
// wait for header and rows to avoid flake town
const headerLoc = table.locator('thead >> role=cell')
Expand Down

0 comments on commit b2b3a74

Please sign in to comment.