diff --git a/app/api/util.ts b/app/api/util.ts index 79a06b87d..fb763d494 100644 --- a/app/api/util.ts +++ b/app/api/util.ts @@ -131,6 +131,10 @@ const instanceActions = { updateNic: ['stopped'], // https://github.com/oxidecomputer/omicron/blob/6dd9802/nexus/src/app/instance.rs#L1520-L1522 serialConsole: ['running', 'rebooting', 'migrating', 'repairing'], + + // https://github.com/oxidecomputer/omicron/blob/5e27bde/nexus/src/app/affinity.rs#L357 checks to see that there's no VMM + // TODO: determine whether the intent is only `stopped` or also `failed` + addToAntiAffinityGroup: ['stopped'], } satisfies Record // setting .states is a cute way to make it ergonomic to call the test function diff --git a/app/pages/project/instances/AntiAffinityCard.tsx b/app/pages/project/instances/AntiAffinityCard.tsx index b6b845696..b113ee9be 100644 --- a/app/pages/project/instances/AntiAffinityCard.tsx +++ b/app/pages/project/instances/AntiAffinityCard.tsx @@ -13,6 +13,7 @@ import * as R from 'remeda' import { apiq, + instanceCan, queryClient, useApiMutation, usePrefetchedQuery, @@ -53,6 +54,12 @@ export const allAntiAffinityGroups = ({ project }: PP.Project) => query: { project, limit: ALL_ISH }, }) +const instanceView = ({ project, instance }: PP.Instance) => + apiq('instanceView', { + path: { instance }, + query: { project }, + }) + const colHelper = createColumnHelper() const staticCols = [ colHelper.accessor('description', Columns.description), @@ -70,6 +77,7 @@ export function AntiAffinityCard() { instanceAntiAffinityGroups(instanceSelector) ) const { data: allGroups } = usePrefetchedQuery(allAntiAffinityGroups(instanceSelector)) + const { data: instanceData } = usePrefetchedQuery(instanceView(instanceSelector)) const nonMemberGroups = useMemo( () => R.differenceWith(allGroups.items, memberGroups.items, (a, b) => a.id === b.id), @@ -143,19 +151,27 @@ export function AntiAffinityCard() { getCoreRowModel: getCoreRowModel(), }) + const getDisabledReason = () => { + if (!instanceCan.addToAntiAffinityGroup(instanceData)) { + return 'This instance must be stopped to add it to a group' + } + if (allGroups.items.length === 0) { + return 'No groups found' + } + if (nonMemberGroups.length === 0) { + return 'Instance is already in all groups' + } + return undefined + } + const disabledReason = getDisabledReason() + return (