diff --git a/Cargo.lock b/Cargo.lock index 478961381a8..2e7dd4f1484 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5263,7 +5263,6 @@ dependencies = [ "anyhow", "dropshot", "hyper", - "installinator-common", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -5271,6 +5270,7 @@ dependencies = [ "serde", "slog", "tufaceous-artifact", + "update-engine", "uuid", ] @@ -5278,7 +5278,6 @@ dependencies = [ name = "installinator-client" version = "0.1.0" dependencies = [ - "installinator-common", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", diff --git a/clients/installinator-client/Cargo.toml b/clients/installinator-client/Cargo.toml index b12ad1b5fa8..33728bbb575 100644 --- a/clients/installinator-client/Cargo.toml +++ b/clients/installinator-client/Cargo.toml @@ -8,7 +8,6 @@ license = "MPL-2.0" workspace = true [dependencies] -installinator-common.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true progenitor.workspace = true diff --git a/clients/installinator-client/src/lib.rs b/clients/installinator-client/src/lib.rs index 35a5e3a274e..8e91bb08235 100644 --- a/clients/installinator-client/src/lib.rs +++ b/clients/installinator-client/src/lib.rs @@ -24,12 +24,10 @@ progenitor::generate_api!( }, replace = { Duration = std::time::Duration, - EventReportForInstallinatorSpec = installinator_common::EventReport, + EventReportForGenericSpec = update_engine::events::EventReport, M2Slot = omicron_common::disk::M2Slot, - ProgressEventForGenericSpec = installinator_common::ProgressEvent, - ProgressEventForInstallinatorSpec = installinator_common::ProgressEvent, - StepEventForGenericSpec = installinator_common::StepEvent, - StepEventForInstallinatorSpec = installinator_common::StepEvent, + ProgressEventForGenericSpec = update_engine::events::ProgressEvent, + StepEventForGenericSpec = update_engine::events::StepEvent, } ); diff --git a/installinator-api/Cargo.toml b/installinator-api/Cargo.toml index f5ef114d986..f6b10268b3a 100644 --- a/installinator-api/Cargo.toml +++ b/installinator-api/Cargo.toml @@ -11,7 +11,6 @@ workspace = true anyhow.workspace = true dropshot.workspace = true hyper.workspace = true -installinator-common.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true @@ -19,4 +18,5 @@ schemars.workspace = true serde.workspace = true slog.workspace = true tufaceous-artifact.workspace = true +update-engine.workspace = true uuid.workspace = true diff --git a/installinator-api/src/lib.rs b/installinator-api/src/lib.rs index cdde1c96a37..98c4e67820b 100644 --- a/installinator-api/src/lib.rs +++ b/installinator-api/src/lib.rs @@ -15,11 +15,11 @@ use dropshot::{ RequestContext, TypedBody, }; use hyper::header; -use installinator_common::EventReport; use omicron_uuid_kinds::MupdateUuid; use schemars::JsonSchema; use serde::Deserialize; use tufaceous_artifact::ArtifactHashId; +use update_engine::{NestedSpec, events::EventReport}; const PROGRESS_REPORT_MAX_BYTES: usize = 4 * 1024 * 1024; @@ -57,7 +57,7 @@ pub trait InstallinatorApi { async fn report_progress( rqctx: RequestContext, path: Path, - report: TypedBody, + report: TypedBody>, ) -> Result; } diff --git a/installinator/src/artifact.rs b/installinator/src/artifact.rs index 405159ebabc..4d188526491 100644 --- a/installinator/src/artifact.rs +++ b/installinator/src/artifact.rs @@ -212,7 +212,7 @@ impl ArtifactClient { report: EventReport, ) -> Result<(), ClientError> { self.client - .report_progress(&update_id, &report) + .report_progress(&update_id, &report.into_generic()) .await .map(|resp| resp.into_inner()) } diff --git a/openapi/installinator.json b/openapi/installinator.json index 748d301151c..f28b6e66fd5 100644 --- a/openapi/installinator.json +++ b/openapi/installinator.json @@ -73,7 +73,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventReportForInstallinatorSpec" + "$ref": "#/components/schemas/EventReportForGenericSpec" } } }, @@ -133,7 +133,7 @@ "request_id" ] }, - "EventReportForInstallinatorSpec": { + "EventReportForGenericSpec": { "description": "A report produced from an [`EventBuffer`](crate::EventBuffer).\n\nRemote reports can be passed into a [`StepContext`](crate::StepContext), in which case they show up as nested events.", "type": "object", "properties": { @@ -148,7 +148,7 @@ "description": "A list of progress events, or whether we're currently waiting for a progress event.\n\nCurrently, this produces one progress event for each top-level and nested event in progress.", "type": "array", "items": { - "$ref": "#/components/schemas/ProgressEventForInstallinatorSpec" + "$ref": "#/components/schemas/ProgressEventForGenericSpec" } }, "root_execution_id": { @@ -161,7 +161,7 @@ "description": "A list of step events.\n\nStep events include success and failure events.", "type": "array", "items": { - "$ref": "#/components/schemas/StepEventForInstallinatorSpec" + "$ref": "#/components/schemas/StepEventForGenericSpec" } } }, @@ -170,246 +170,6 @@ "step_events" ] }, - "InstallinatorCompletionMetadata": { - "oneOf": [ - { - "type": "object", - "properties": { - "disks_found": { - "description": "Number of disks found.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "reason": { - "type": "string", - "enum": [ - "hardware_scan" - ] - } - }, - "required": [ - "disks_found", - "reason" - ] - }, - { - "type": "object", - "properties": { - "reason": { - "type": "string", - "enum": [ - "control_plane_zones" - ] - }, - "zones_to_install": { - "description": "Number of zone images that will be installed.", - "type": "integer", - "format": "uint", - "minimum": 0 - } - }, - "required": [ - "reason", - "zones_to_install" - ] - }, - { - "type": "object", - "properties": { - "address": { - "description": "The address the artifact was downloaded from.", - "type": "string" - }, - "reason": { - "type": "string", - "enum": [ - "download" - ] - } - }, - "required": [ - "address", - "reason" - ] - }, - { - "type": "object", - "properties": { - "output": { - "description": "The output of the write operation.", - "allOf": [ - { - "$ref": "#/components/schemas/WriteOutput" - } - ] - }, - "reason": { - "type": "string", - "enum": [ - "write" - ] - } - }, - "required": [ - "output", - "reason" - ] - }, - { - "description": "Future variants that might be unknown.", - "type": "object", - "properties": { - "reason": { - "type": "string", - "enum": [ - "unknown" - ] - } - }, - "required": [ - "reason" - ] - } - ] - }, - "InstallinatorComponent": { - "description": "Installinator components.", - "oneOf": [ - { - "description": "The installinator document.", - "type": "string", - "enum": [ - "installinator_document" - ] - }, - { - "description": "The host phase 2 component.", - "type": "string", - "enum": [ - "host_phase2" - ] - }, - { - "description": "The control plane component.", - "type": "string", - "enum": [ - "control_plane" - ] - }, - { - "description": "A component that means \"both the host and the control plane\", used for writes for now. It is possible that this component will go away in the future.", - "type": "string", - "enum": [ - "both" - ] - }, - { - "description": "Future variants that might be unknown.", - "type": "string", - "enum": [ - "unknown" - ] - } - ] - }, - "InstallinatorProgressMetadata": { - "oneOf": [ - { - "type": "object", - "properties": { - "peer": { - "description": "The peer being downloaded from.\n\nAvailable with downlad events.", - "type": "string" - }, - "reason": { - "type": "string", - "enum": [ - "download" - ] - } - }, - "required": [ - "peer", - "reason" - ] - }, - { - "description": "Future variants that might be unknown.", - "type": "object", - "properties": { - "reason": { - "type": "string", - "enum": [ - "unknown" - ] - } - }, - "required": [ - "reason" - ] - } - ] - }, - "InstallinatorStepId": { - "description": "Installinator step identifier.", - "type": "string", - "enum": [ - "download", - "format", - "scan", - "unpack_control_plane_artifact", - "write" - ] - }, - "InstallinatorStepMetadata": { - "oneOf": [ - { - "type": "object", - "properties": { - "destination": { - "nullable": true, - "description": "The destination being formatted or written to.\n\nAvailable with format and destination events.", - "type": "string", - "format": "Utf8PathBuf" - }, - "reason": { - "type": "string", - "enum": [ - "write" - ] - } - }, - "required": [ - "destination", - "reason" - ] - }, - { - "description": "Future variants that might be unknown.", - "type": "object", - "properties": { - "reason": { - "type": "string", - "enum": [ - "unknown" - ] - } - }, - "required": [ - "reason" - ] - } - ] - }, - "M2Slot": { - "description": "Describes an M.2 slot, often in the context of writing a system image to it.", - "type": "string", - "enum": [ - "A", - "B" - ] - }, "ProgressCounter": { "description": "Current progress.\n\nBoth `current` and `total` are abstract counters. These counters are often a number of bytes. There is no guarantee that the counter won't go back in subsequent events; that can happen e.g. if a fetch happens from multiple peers within a single attempt.", "type": "object", @@ -473,42 +233,6 @@ "total_elapsed" ] }, - "ProgressEventForInstallinatorSpec": { - "type": "object", - "properties": { - "data": { - "description": "The kind of event this is.", - "allOf": [ - { - "$ref": "#/components/schemas/ProgressEventKindForInstallinatorSpec" - } - ] - }, - "execution_id": { - "description": "The execution ID.", - "type": "string", - "format": "uuid" - }, - "spec": { - "description": "The specification that this event belongs to.\n\nThis is typically the name of the type `S` for which `StepSpec` is implemented.\n\nThis can be used with `Self::from_generic` to deserialize generic metadata.", - "type": "string" - }, - "total_elapsed": { - "description": "Total time elapsed since the start of execution.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "data", - "execution_id", - "spec", - "total_elapsed" - ] - }, "ProgressEventKindForGenericSpec": { "oneOf": [ { @@ -601,809 +325,7 @@ { "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } - ] - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "attempt", - "attempt_elapsed", - "kind", - "metadata", - "step", - "step_elapsed" - ] - }, - { - "type": "object", - "properties": { - "attempt": { - "description": "The attempt number currently being executed.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "attempt_elapsed": { - "description": "The time it took for this attempt to complete.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "event": { - "description": "The event that occurred.", - "allOf": [ - { - "$ref": "#/components/schemas/ProgressEventForGenericSpec" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "nested" - ] - }, - "step": { - "description": "Information about the step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" - } - ] - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "attempt", - "attempt_elapsed", - "event", - "kind", - "step", - "step_elapsed" - ] - }, - { - "description": "Future variants that might be unknown.", - "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": [ - "unknown" - ] - } - }, - "required": [ - "kind" - ] - } - ] - }, - "ProgressEventKindForInstallinatorSpec": { - "oneOf": [ - { - "description": "The update engine is waiting for a progress message.\n\nThe update engine sends this message immediately after a [`StepEvent`] corresponding to a new step.", - "type": "object", - "properties": { - "attempt": { - "description": "The attempt number currently being executed.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "attempt_elapsed": { - "description": "Total time elapsed since the start of the attempt.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "waiting_for_progress" - ] - }, - "step": { - "description": "Information about the step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" - } - ] - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "attempt", - "attempt_elapsed", - "kind", - "step", - "step_elapsed" - ] - }, - { - "type": "object", - "properties": { - "attempt": { - "description": "The attempt number currently being executed.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "attempt_elapsed": { - "description": "Total time elapsed since the start of the attempt.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "progress" - ] - }, - "metadata": { - "description": "Metadata that was returned with progress.", - "allOf": [ - { - "$ref": "#/components/schemas/InstallinatorProgressMetadata" - } - ] - }, - "progress": { - "nullable": true, - "description": "Current progress.", - "allOf": [ - { - "$ref": "#/components/schemas/ProgressCounter" - } - ] - }, - "step": { - "description": "Information about the step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" - } - ] - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "attempt", - "attempt_elapsed", - "kind", - "metadata", - "step", - "step_elapsed" - ] - }, - { - "type": "object", - "properties": { - "attempt": { - "description": "The attempt number currently being executed.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "attempt_elapsed": { - "description": "The time it took for this attempt to complete.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "event": { - "description": "The event that occurred.", - "allOf": [ - { - "$ref": "#/components/schemas/ProgressEventForGenericSpec" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "nested" - ] - }, - "step": { - "description": "Information about the step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" - } - ] - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "attempt", - "attempt_elapsed", - "event", - "kind", - "step", - "step_elapsed" - ] - }, - { - "description": "Future variants that might be unknown.", - "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": [ - "unknown" - ] - } - }, - "required": [ - "kind" - ] - } - ] - }, - "StepComponentSummaryForGenericSpec": { - "type": "object", - "properties": { - "component": { - "description": "The component." - }, - "total_component_steps": { - "description": "The number of steps present in this component.", - "type": "integer", - "format": "uint", - "minimum": 0 - } - }, - "required": [ - "component", - "total_component_steps" - ] - }, - "StepComponentSummaryForInstallinatorSpec": { - "type": "object", - "properties": { - "component": { - "description": "The component.", - "allOf": [ - { - "$ref": "#/components/schemas/InstallinatorComponent" - } - ] - }, - "total_component_steps": { - "description": "The number of steps present in this component.", - "type": "integer", - "format": "uint", - "minimum": 0 - } - }, - "required": [ - "component", - "total_component_steps" - ] - }, - "StepEventForGenericSpec": { - "type": "object", - "properties": { - "data": { - "description": "The kind of event this is.", - "allOf": [ - { - "$ref": "#/components/schemas/StepEventKindForGenericSpec" - } - ] - }, - "event_index": { - "description": "A monotonically increasing index for this `StepEvent`.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "execution_id": { - "description": "The execution ID.", - "type": "string", - "format": "uuid" - }, - "spec": { - "description": "The specification that this event belongs to.\n\nThis is typically the name of the type `S` for which `StepSpec` is implemented.\n\nThis can be used along with `Self::from_generic` to identify which specification to deserialize generic metadata against. For example:\n\n```rust,ignore if event.spec == \"MySpec\" { // event is likely generated from a MySpec engine. let event = Event::::from_generic(event)?; // ... } ```", - "type": "string" - }, - "total_elapsed": { - "description": "Total time elapsed since the start of execution.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "data", - "event_index", - "execution_id", - "spec", - "total_elapsed" - ] - }, - "StepEventForInstallinatorSpec": { - "type": "object", - "properties": { - "data": { - "description": "The kind of event this is.", - "allOf": [ - { - "$ref": "#/components/schemas/StepEventKindForInstallinatorSpec" - } - ] - }, - "event_index": { - "description": "A monotonically increasing index for this `StepEvent`.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "execution_id": { - "description": "The execution ID.", - "type": "string", - "format": "uuid" - }, - "spec": { - "description": "The specification that this event belongs to.\n\nThis is typically the name of the type `S` for which `StepSpec` is implemented.\n\nThis can be used along with `Self::from_generic` to identify which specification to deserialize generic metadata against. For example:\n\n```rust,ignore if event.spec == \"MySpec\" { // event is likely generated from a MySpec engine. let event = Event::::from_generic(event)?; // ... } ```", - "type": "string" - }, - "total_elapsed": { - "description": "Total time elapsed since the start of execution.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "data", - "event_index", - "execution_id", - "spec", - "total_elapsed" - ] - }, - "StepEventKindForGenericSpec": { - "oneOf": [ - { - "description": "No steps were defined, and the executor exited without doing anything.\n\nThis is a terminal event: it is guaranteed that no more events will be seen after this one.", - "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": [ - "no_steps_defined" - ] - } - }, - "required": [ - "kind" - ] - }, - { - "description": "Execution was started.\n\nThis is an initial event -- it is always expected to be the first event received from the event stream.", - "type": "object", - "properties": { - "components": { - "description": "A list of components, along with the number of items each component has.", - "type": "array", - "items": { - "$ref": "#/components/schemas/StepComponentSummaryForGenericSpec" - } - }, - "first_step": { - "description": "Information about the first step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "execution_started" - ] - }, - "steps": { - "description": "The list of steps that will be executed.", - "type": "array", - "items": { - "$ref": "#/components/schemas/StepInfoForGenericSpec" - } - } - }, - "required": [ - "components", - "first_step", - "kind", - "steps" - ] - }, - { - "description": "Progress was reset along an attempt, and this attempt is going down a different path.", - "type": "object", - "properties": { - "attempt": { - "description": "The current attempt number.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "attempt_elapsed": { - "description": "The amount of time this attempt has taken so far.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "progress_reset" - ] - }, - "message": { - "description": "A message assocaited with the reset.", - "type": "string" - }, - "metadata": { - "description": "Progress-related metadata associated with this attempt." - }, - "step": { - "description": "Information about the step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" - } - ] - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "attempt", - "attempt_elapsed", - "kind", - "message", - "metadata", - "step", - "step_elapsed" - ] - }, - { - "description": "An attempt failed and this step is being retried.", - "type": "object", - "properties": { - "attempt_elapsed": { - "description": "The amount of time the previous attempt took.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "attempt_retry" - ] - }, - "message": { - "description": "A message associated with the retry.", - "type": "string" - }, - "next_attempt": { - "description": "The attempt number for the next attempt.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "step": { - "description": "Information about the step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" - } - ] - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "attempt_elapsed", - "kind", - "message", - "next_attempt", - "step", - "step_elapsed" - ] - }, - { - "description": "A step is complete and the next step has been started.", - "type": "object", - "properties": { - "attempt": { - "description": "The attempt number that completed.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "attempt_elapsed": { - "description": "The time it took for this attempt to complete.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "step_completed" - ] - }, - "next_step": { - "description": "The next step that is being started.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" - } - ] - }, - "outcome": { - "description": "The outcome of the step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepOutcomeForGenericSpec" - } - ] - }, - "step": { - "description": "Information about the step that just completed.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" - } - ] - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "attempt", - "attempt_elapsed", - "kind", - "next_step", - "outcome", - "step", - "step_elapsed" - ] - }, - { - "description": "Execution is complete.\n\nThis is a terminal event: it is guaranteed that no more events will be seen after this one.", - "type": "object", - "properties": { - "attempt_elapsed": { - "description": "The time it took for this attempt to complete.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "execution_completed" - ] - }, - "last_attempt": { - "description": "The attempt number that completed.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "last_outcome": { - "description": "The outcome of the last step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepOutcomeForGenericSpec" - } - ] - }, - "last_step": { - "description": "Information about the last step that completed.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" - } - ] - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - } - }, - "required": [ - "attempt_elapsed", - "kind", - "last_attempt", - "last_outcome", - "last_step", - "step_elapsed" - ] - }, - { - "description": "Execution failed.\n\nThis is a terminal event: it is guaranteed that no more events will be seen after this one.", - "type": "object", - "properties": { - "attempt_elapsed": { - "description": "The time it took for this attempt to complete.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "causes": { - "description": "A chain of causes associated with the failure.", - "type": "array", - "items": { - "type": "string" - } - }, - "failed_step": { - "description": "Information about the step that failed.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "execution_failed" - ] - }, - "message": { - "description": "A message associated with the failure.", - "type": "string" - }, - "step_elapsed": { - "description": "Total time elapsed since the start of the step. Includes prior attempts.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "total_attempts": { - "description": "The total number of attempts that were performed before the step failed.", - "type": "integer", - "format": "uint", - "minimum": 0 - } - }, - "required": [ - "attempt_elapsed", - "causes", - "failed_step", - "kind", - "message", - "step_elapsed", - "total_attempts" - ] - }, - { - "description": "Execution aborted by an external user.\n\nThis is a terminal event: it is guaranteed that no more events will be seen after this one.", - "type": "object", - "properties": { - "aborted_step": { - "description": "Information about the step that was running at the time execution was aborted.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" - } - ] - }, - "attempt": { - "description": "The attempt that was running at the time the step was aborted.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "attempt_elapsed": { - "description": "The time it took for this attempt to complete.", - "allOf": [ - { - "$ref": "#/components/schemas/Duration" - } - ] - }, - "kind": { - "type": "string", - "enum": [ - "execution_aborted" - ] - }, - "message": { - "description": "A message associated with the abort.", - "type": "string" + ] }, "step_elapsed": { "description": "Total time elapsed since the start of the step. Includes prior attempts.", @@ -1415,20 +337,19 @@ } }, "required": [ - "aborted_step", "attempt", "attempt_elapsed", "kind", - "message", + "metadata", + "step", "step_elapsed" ] }, { - "description": "A nested step event occurred.", "type": "object", "properties": { "attempt": { - "description": "The current attempt number.", + "description": "The attempt number currently being executed.", "type": "integer", "format": "uint", "minimum": 0 @@ -1445,7 +366,7 @@ "description": "The event that occurred.", "allOf": [ { - "$ref": "#/components/schemas/StepEventForGenericSpec" + "$ref": "#/components/schemas/ProgressEventForGenericSpec" } ] }, @@ -1456,7 +377,7 @@ ] }, "step": { - "description": "Information about the step that's occurring.", + "description": "Information about the step.", "allOf": [ { "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" @@ -1498,7 +419,68 @@ } ] }, - "StepEventKindForInstallinatorSpec": { + "StepComponentSummaryForGenericSpec": { + "type": "object", + "properties": { + "component": { + "description": "The component." + }, + "total_component_steps": { + "description": "The number of steps present in this component.", + "type": "integer", + "format": "uint", + "minimum": 0 + } + }, + "required": [ + "component", + "total_component_steps" + ] + }, + "StepEventForGenericSpec": { + "type": "object", + "properties": { + "data": { + "description": "The kind of event this is.", + "allOf": [ + { + "$ref": "#/components/schemas/StepEventKindForGenericSpec" + } + ] + }, + "event_index": { + "description": "A monotonically increasing index for this `StepEvent`.", + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "execution_id": { + "description": "The execution ID.", + "type": "string", + "format": "uuid" + }, + "spec": { + "description": "The specification that this event belongs to.\n\nThis is typically the name of the type `S` for which `StepSpec` is implemented.\n\nThis can be used along with `Self::from_generic` to identify which specification to deserialize generic metadata against. For example:\n\n```rust,ignore if event.spec == \"MySpec\" { // event is likely generated from a MySpec engine. let event = Event::::from_generic(event)?; // ... } ```", + "type": "string" + }, + "total_elapsed": { + "description": "Total time elapsed since the start of execution.", + "allOf": [ + { + "$ref": "#/components/schemas/Duration" + } + ] + } + }, + "required": [ + "data", + "event_index", + "execution_id", + "spec", + "total_elapsed" + ] + }, + "StepEventKindForGenericSpec": { "oneOf": [ { "description": "No steps were defined, and the executor exited without doing anything.\n\nThis is a terminal event: it is guaranteed that no more events will be seen after this one.", @@ -1523,14 +505,14 @@ "description": "A list of components, along with the number of items each component has.", "type": "array", "items": { - "$ref": "#/components/schemas/StepComponentSummaryForInstallinatorSpec" + "$ref": "#/components/schemas/StepComponentSummaryForGenericSpec" } }, "first_step": { "description": "Information about the first step.", "allOf": [ { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } ] }, @@ -1544,7 +526,7 @@ "description": "The list of steps that will be executed.", "type": "array", "items": { - "$ref": "#/components/schemas/StepInfoForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoForGenericSpec" } } }, @@ -1584,18 +566,13 @@ "type": "string" }, "metadata": { - "description": "Progress-related metadata associated with this attempt.", - "allOf": [ - { - "$ref": "#/components/schemas/InstallinatorProgressMetadata" - } - ] + "description": "Progress-related metadata associated with this attempt." }, "step": { "description": "Information about the step.", "allOf": [ { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } ] }, @@ -1650,7 +627,7 @@ "description": "Information about the step.", "allOf": [ { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } ] }, @@ -1700,7 +677,7 @@ "description": "The next step that is being started.", "allOf": [ { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } ] }, @@ -1708,7 +685,7 @@ "description": "The outcome of the step.", "allOf": [ { - "$ref": "#/components/schemas/StepOutcomeForInstallinatorSpec" + "$ref": "#/components/schemas/StepOutcomeForGenericSpec" } ] }, @@ -1716,7 +693,7 @@ "description": "Information about the step that just completed.", "allOf": [ { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } ] }, @@ -1767,7 +744,7 @@ "description": "The outcome of the last step.", "allOf": [ { - "$ref": "#/components/schemas/StepOutcomeForInstallinatorSpec" + "$ref": "#/components/schemas/StepOutcomeForGenericSpec" } ] }, @@ -1775,7 +752,7 @@ "description": "Information about the last step that completed.", "allOf": [ { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } ] }, @@ -1820,7 +797,7 @@ "description": "Information about the step that failed.", "allOf": [ { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } ] }, @@ -1867,7 +844,7 @@ "description": "Information about the step that was running at the time execution was aborted.", "allOf": [ { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } ] }, @@ -1949,7 +926,7 @@ "description": "Information about the step that's occurring.", "allOf": [ { - "$ref": "#/components/schemas/StepInfoWithMetadataForInstallinatorSpec" + "$ref": "#/components/schemas/StepInfoWithMetadataForGenericSpec" } ] }, @@ -2030,58 +1007,6 @@ "total_component_steps" ] }, - "StepInfoForInstallinatorSpec": { - "description": "Serializable information about a step.", - "type": "object", - "properties": { - "component": { - "description": "The component that this step is part of.", - "allOf": [ - { - "$ref": "#/components/schemas/InstallinatorComponent" - } - ] - }, - "component_index": { - "description": "The index of the step within the component.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "description": { - "description": "The description for this step.", - "type": "string" - }, - "id": { - "description": "An identifier for this step.", - "allOf": [ - { - "$ref": "#/components/schemas/InstallinatorStepId" - } - ] - }, - "index": { - "description": "The index of the step within all steps to be executed.", - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "total_component_steps": { - "description": "The total number of steps in this component.", - "type": "integer", - "format": "uint", - "minimum": 0 - } - }, - "required": [ - "component", - "component_index", - "description", - "id", - "index", - "total_component_steps" - ] - }, "StepInfoWithMetadataForGenericSpec": { "description": "Serializable information about a step.", "type": "object", @@ -2103,32 +1028,6 @@ "info" ] }, - "StepInfoWithMetadataForInstallinatorSpec": { - "description": "Serializable information about a step.", - "type": "object", - "properties": { - "info": { - "description": "Information about this step.", - "allOf": [ - { - "$ref": "#/components/schemas/StepInfoForInstallinatorSpec" - } - ] - }, - "metadata": { - "nullable": true, - "description": "Additional metadata associated with this step.", - "allOf": [ - { - "$ref": "#/components/schemas/InstallinatorStepMetadata" - } - ] - } - }, - "required": [ - "info" - ] - }, "StepOutcomeForGenericSpec": { "oneOf": [ { @@ -2205,118 +1104,6 @@ } ] }, - "StepOutcomeForInstallinatorSpec": { - "oneOf": [ - { - "description": "The step completed successfully.", - "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": [ - "success" - ] - }, - "message": { - "nullable": true, - "description": "An optional message associated with this step.", - "type": "string" - }, - "metadata": { - "nullable": true, - "description": "Optional completion metadata associated with the step.", - "allOf": [ - { - "$ref": "#/components/schemas/InstallinatorCompletionMetadata" - } - ] - } - }, - "required": [ - "kind" - ] - }, - { - "description": "The step completed with a warning.", - "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": [ - "warning" - ] - }, - "message": { - "description": "A warning message.", - "type": "string" - }, - "metadata": { - "nullable": true, - "description": "Optional completion metadata associated with the step.", - "allOf": [ - { - "$ref": "#/components/schemas/InstallinatorCompletionMetadata" - } - ] - } - }, - "required": [ - "kind", - "message" - ] - }, - { - "description": "The step was skipped with a message.", - "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": [ - "skipped" - ] - }, - "message": { - "description": "Message associated with the skip.", - "type": "string" - }, - "metadata": { - "nullable": true, - "description": "Optional metadata associated with the skip." - } - }, - "required": [ - "kind", - "message" - ] - } - ] - }, - "WriteOutput": { - "description": "The output of a write operation.\n\nForms part of [`InstallinatorCompletionMetadata::Write`].", - "type": "object", - "properties": { - "slots_attempted": { - "description": "The slots that were requested to be written.", - "type": "array", - "items": { - "$ref": "#/components/schemas/M2Slot" - }, - "uniqueItems": true - }, - "slots_written": { - "description": "The slots that were actually written.", - "type": "array", - "items": { - "$ref": "#/components/schemas/M2Slot" - }, - "uniqueItems": true - } - }, - "required": [ - "slots_attempted", - "slots_written" - ] - }, "MupdateUuid": { "x-rust-type": { "crate": "omicron-uuid-kinds", diff --git a/wicketd/src/artifacts/server.rs b/wicketd/src/artifacts/server.rs index c3890948641..1bb4f28ef99 100644 --- a/wicketd/src/artifacts/server.rs +++ b/wicketd/src/artifacts/server.rs @@ -16,10 +16,11 @@ use futures::TryStreamExt; use installinator_api::InstallinatorApi; use installinator_api::ReportQuery; use installinator_api::body_to_artifact_response; -use installinator_common::EventReport; use slog::Logger; use slog::error; use tufaceous_artifact::ArtifactHashId; +use update_engine::NestedSpec; +use update_engine::events::EventReport; use super::WicketdArtifactStore; @@ -89,7 +90,7 @@ impl InstallinatorApi for WicketdInstallinatorApiImpl { async fn report_progress( rqctx: RequestContext, path: Path, - report: TypedBody, + report: TypedBody>, ) -> Result { let context = rqctx.context(); let update_id = path.into_inner().update_id; diff --git a/wicketd/src/installinator_progress.rs b/wicketd/src/installinator_progress.rs index 51160140869..e99a77ed3e6 100644 --- a/wicketd/src/installinator_progress.rs +++ b/wicketd/src/installinator_progress.rs @@ -15,7 +15,10 @@ use std::{ use installinator_api::EventReportStatus; use omicron_uuid_kinds::MupdateUuid; use tokio::sync::{oneshot, watch}; -use update_engine::events::StepEventIsTerminal; +use update_engine::{ + NestedSpec, + events::{EventReport, StepEventIsTerminal}, +}; /// Creates the artifact server and update tracker's interfaces to the /// installinator progress tracker. @@ -52,7 +55,7 @@ impl IprArtifactServer { pub(crate) fn report_progress( &self, update_id: MupdateUuid, - report: installinator_common::EventReport, + report: EventReport, ) -> EventReportStatus { let mut running_updates = self.running_updates.lock().unwrap(); if let Some(update) = running_updates.get_mut(&update_id) { @@ -154,22 +157,20 @@ impl IprUpdateTracker { /// Type alias for the receiver that resolves when the first message from the /// installinator has been received. pub(crate) type IprStartReceiver = - oneshot::Receiver>; + oneshot::Receiver>>; #[derive(Debug)] #[must_use] enum RunningUpdate { /// This is the initial state: the first message from the installinator /// hasn't been received yet. - Initial( - oneshot::Sender>, - ), + Initial(oneshot::Sender>>), /// Reports from the installinator have been received. /// /// This is an `UnboundedSender` to avoid cancel-safety issues (see /// ). - ReportsReceived(watch::Sender), + ReportsReceived(watch::Sender>), /// All messages have been received. /// @@ -202,14 +203,15 @@ impl RunningUpdate { } } } + fn take(&mut self) -> Self { std::mem::replace(self, Self::Invalid) } fn send_and_next_state( log: &slog::Logger, - sender: watch::Sender, - report: installinator_common::EventReport, + sender: watch::Sender>, + report: EventReport, ) -> (Self, EventReportStatus) { let is_terminal = Self::is_terminal(&report); match sender.send(report) { @@ -257,7 +259,7 @@ impl RunningUpdate { } } - fn is_terminal(report: &installinator_common::EventReport) -> bool { + fn is_terminal(report: &EventReport) -> bool { report .step_events .last() @@ -362,8 +364,14 @@ mod tests { kind: StepEventKind::ExecutionCompleted { last_step: StepInfoWithMetadata { info: StepInfo { - id: InstallinatorStepId::Write, - component: InstallinatorComponent::Both, + id: serde_json::to_value( + InstallinatorStepId::Write, + ) + .expect("serialized step ID"), + component: serde_json::to_value( + InstallinatorComponent::Both, + ) + .expect("serialized component"), description: "Fake step".into(), index: 0, component_index: 0, @@ -375,16 +383,22 @@ mod tests { last_outcome: StepOutcome::Success { message: Some("Message".into()), metadata: Some( - InstallinatorCompletionMetadata::Write { - output: WriteOutput { - slots_attempted: vec![M2Slot::A, M2Slot::B] - .into_iter() - .collect(), - slots_written: vec![M2Slot::A] + serde_json::to_value( + InstallinatorCompletionMetadata::Write { + output: WriteOutput { + slots_attempted: vec![ + M2Slot::A, + M2Slot::B, + ] .into_iter() .collect(), + slots_written: vec![M2Slot::A] + .into_iter() + .collect(), + }, }, - }, + ) + .expect("serialized metadata"), ), }, step_elapsed: Duration::from_secs(1), diff --git a/wicketd/src/update_tracker.rs b/wicketd/src/update_tracker.rs index 40336b46848..9a89d8769ee 100644 --- a/wicketd/src/update_tracker.rs +++ b/wicketd/src/update_tracker.rs @@ -34,7 +34,6 @@ use gateway_messages::ROT_PAGE_SIZE; use gateway_messages::SpComponent; use hubtools::RawHubrisArchive; use installinator_common::InstallinatorCompletionMetadata; -use installinator_common::InstallinatorSpec; use installinator_common::WriteOutput; use omicron_common::disk::M2Slot; use omicron_uuid_kinds::MupdateUuid; @@ -66,10 +65,10 @@ use tufaceous_artifact::ArtifactVersion; use update_common::artifacts::ArtifactIdData; use update_common::artifacts::ArtifactsWithPlan; use update_common::artifacts::ControlPlaneZonesMode; - use update_common::artifacts::UpdatePlan; use update_common::artifacts::VerificationMode; use update_engine::AbortHandle; +use update_engine::NestedSpec; use update_engine::StepSpec; use update_engine::events::ProgressUnits; use uuid::Uuid; @@ -1835,7 +1834,7 @@ impl UpdateContext { async fn process_installinator_reports( &self, cx: &StepContext, - mut ipr_receiver: watch::Receiver>, + mut ipr_receiver: watch::Receiver>, ) -> anyhow::Result { let mut write_output = None; @@ -1863,6 +1862,14 @@ impl UpdateContext { continue; }; + let metadata = serde_json::from_value::< + InstallinatorCompletionMetadata, + >(metadata.clone()) + .context( + "failed to deserialize completion metadata \ + even though there's an #[unknown] variant", + )?; + match metadata { InstallinatorCompletionMetadata::Write { output } => { write_output = Some(output.clone()); @@ -2395,7 +2402,7 @@ impl UpdateContext { cx: &StepContext, mut ipr_start_receiver: IprStartReceiver, image_id: HostPhase2RecoveryImageId, - ) -> anyhow::Result>> { + ) -> anyhow::Result>> { const MGS_PROGRESS_POLL_INTERVAL: Duration = Duration::from_secs(3); // Waiting for the installinator to start is a little strange. It can't