diff --git a/.github/workflows/packer.yaml b/.github/workflows/packer.yaml index 5d4d159425..5d1f20455d 100644 --- a/.github/workflows/packer.yaml +++ b/.github/workflows/packer.yaml @@ -75,7 +75,7 @@ jobs: CLOUDFLARE_TOKEN: ${{secrets.CLOUDFLARE_TOKEN}} SSL_CERT: ${{secrets.SSL_CERT}} SSL_KEY: ${{secrets.SSL_KEY}} - API_VERSION: a75149c4b84b51647928c2c3484dc4370495f023 + API_VERSION: c4e76cb01fa791c4dc9722072800c8969397aa03 # get the image information from gcloud - name: Get image information diff --git a/app/components/StatusBadge.tsx b/app/components/StatusBadge.tsx index cb249d797b..78f7b5b07a 100644 --- a/app/components/StatusBadge.tsx +++ b/app/components/StatusBadge.tsx @@ -12,6 +12,7 @@ const INSTANCE_COLORS: Record = { stopping: 'yellow', stopped: 'lightGray', repairing: 'blue', + migrating: 'yellow', failed: 'red', destroyed: 'darkGray', } diff --git a/libs/api/__generated__/Api.ts b/libs/api/__generated__/Api.ts index c4b44070d2..fd81277376 100644 --- a/libs/api/__generated__/Api.ts +++ b/libs/api/__generated__/Api.ts @@ -180,6 +180,13 @@ export type InstanceCreate = { ncpus: InstanceCpuCount } +/** + * Migration parameters for an [`Instance`] + */ +export type InstanceMigrate = { + dstSledUuid: string +} + /** * A single page of results */ @@ -206,6 +213,7 @@ export type InstanceState = | 'stopping' | 'stopped' | 'rebooting' + | 'migrating' | 'repairing' | 'failed' | 'destroyed' @@ -1338,6 +1346,14 @@ export interface InstanceDisksDetachParams { projectName: Name } +export interface ProjectInstancesMigrateInstanceParams { + instanceName: Name + + orgName: Name + + projectName: Name +} + export interface ProjectInstancesInstanceRebootParams { instanceName: Name @@ -2037,7 +2053,6 @@ export class Api extends HttpClient { /** * Update a specific organization. - * * TODO-correctness: Is it valid for PUT to accept application/json that's a subset of what the resource actually represents? If not, is that a problem? (HTTP may require that this be idempotent.) If so, can we get around that having this be a slightly different content-type (e.g., "application/json-patch")? We should see what other APIs do. */ organizationsPutOrganization: ( { orgName }: OrganizationsPutOrganizationParams, @@ -2108,7 +2123,6 @@ export class Api extends HttpClient { /** * Update a specific project. - * * TODO-correctness: Is it valid for PUT to accept application/json that's a subset of what the resource actually represents? If not, is that a problem? (HTTP may require that this be idempotent.) If so, can we get around that having this be a slightly different content-type (e.g., "application/json-patch")? We should see what other APIs do. */ organizationProjectsPutProject: ( { orgName, projectName }: OrganizationProjectsPutProjectParams, @@ -2151,7 +2165,6 @@ export class Api extends HttpClient { /** * Create a disk in a project. - * * TODO-correctness See note about instance create. This should be async. */ projectDisksPost: ( { orgName, projectName }: ProjectDisksPostParams, @@ -2207,7 +2220,6 @@ export class Api extends HttpClient { /** * Create an instance in a project. - * * TODO-correctness This is supposed to be async. Is that right? We can create the instance immediately -- it's just not booted yet. Maybe the boot operation is what's a separate operation_id. What about the response code (201 Created vs 202 Accepted)? Is that orthogonal? Things can return a useful response, including an operation id, with either response code. Maybe a "reboot" operation would return a 202 Accepted because there's no actual resource created? */ projectInstancesPost: ( { orgName, projectName }: ProjectInstancesPostParams, @@ -2289,6 +2301,25 @@ export class Api extends HttpClient { ...params, }), + /** + * Migrate an instance to a different propolis-server, possibly on a different sled. + */ + projectInstancesMigrateInstance: ( + { + instanceName, + orgName, + projectName, + }: ProjectInstancesMigrateInstanceParams, + data: InstanceMigrate, + params: RequestParams = {} + ) => + this.request({ + path: `/organizations/${orgName}/projects/${projectName}/instances/${instanceName}/migrate`, + method: 'POST', + body: data, + ...params, + }), + /** * Reboot an instance. */ diff --git a/libs/api/__generated__/OMICRON_VERSION b/libs/api/__generated__/OMICRON_VERSION index 4c77980788..6f9fadd6e8 100644 --- a/libs/api/__generated__/OMICRON_VERSION +++ b/libs/api/__generated__/OMICRON_VERSION @@ -1,2 +1,2 @@ # generated file. do not update manually. see docs/update-pinned-api.md -a75149c4b84b51647928c2c3484dc4370495f023 +c4e76cb01fa791c4dc9722072800c8969397aa03 diff --git a/packer/oxapi_demo b/packer/oxapi_demo index bc24146a8f..fdd1395ff5 100755 --- a/packer/oxapi_demo +++ b/packer/oxapi_demo @@ -39,6 +39,7 @@ INSTANCES instance_create_demo ORGANIZATION_NAME PROJECT_NAME INSTANCE_NAME instance_get ORGANIZATION_NAME PROJECT_NAME INSTANCE_NAME instance_delete ORGANIZATION_NAME PROJECT_NAME INSTANCE_NAME + instance_migrate ORGANIZATION_NAME PROJECT_NAME INSTANCE_NAME SLED_ID instance_stop ORGANIZATION_NAME PROJECT_NAME INSTANCE_NAME instance_start ORGANIZATION_NAME PROJECT_NAME INSTANCE_NAME @@ -278,6 +279,13 @@ function cmd_instance_delete do_curl "/organizations/$1/projects/$2/instances/$3" -X DELETE } +function cmd_instance_migrate +{ + [[ $# != 4 ]] && usage "expected ORGANIZATION_NAME PROJECT_NAME INSTANCE_NAME SLED_ID" + mkjson dst_sled_uuid="$4" | \ + do_curl "/organizations/$1/projects/$2/instances/$3/migrate" -X POST -T - +} + function cmd_instance_stop { [[ $# != 3 ]] && usage "expected ORGANIZATION_NAME PROJECT_NAME INSTANCE_NAME" diff --git a/tools/create_gcp_instance.sh b/tools/create_gcp_instance.sh index 8e813957e8..24a4cca3f5 100755 --- a/tools/create_gcp_instance.sh +++ b/tools/create_gcp_instance.sh @@ -41,7 +41,7 @@ retry 2 gcloud compute instances create "$INSTANCE_NAME" \ --description="Machine automatically generated from branch ${BRANCH_NAME} of the oxidecomputer/console git repo." \ --hostname="${INSTANCE_NAME}.internal.oxide.computer" \ --zone=$ZONE \ - --image=packer-1643233699 \ + --image=packer-1643643314 \ --maintenance-policy=TERMINATE \ --restart-on-failure \ --machine-type=$INSTANCE_TYPE \