From e3ae992f47310da4a9f97b14de5cc9fd8dfa616a Mon Sep 17 00:00:00 2001 From: Andrii Melashchenko <6060861+javatask@users.noreply.github.com> Date: Sun, 26 Jul 2026 22:15:48 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(compose):=20implement=20SUP-01=20Compo?= =?UTF-8?q?se=20OCI=20=E2=80=94=20The=20Margo=20Way?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Specification Update Proposal SUP-01 which defines the normative Compose Archive packaging model for Margo, following the same OCI registry pattern established for Helm components. Schema changes (application-description.linkml.yaml): - Make repository and revision required for all component types - Add SemVer 2.0 pattern constraint on revision field - Remove packageLocation and keyLocation fields (never released) Schema changes (desired-state.linkml.yaml): - Add optional revision attribute to Component class with SemVer pattern Documentation (application-registry.md): - Add Compose-specific OCI media types to Margo-Specific Media Types table (application/vnd.org.margo.component.compose+json and application/vnd.org.margo.component.compose.tar+gzip) - Add normative Compose Archive Structure section (directory layout, security constraints, integrity verification) - Add publishing workflow guidance (oras push recommended) - Add wait semantics for Compose components Documentation (index.md.jinja2): - Update ComponentProperties compose table to document repository/revision - Remove Investigation Needed block for compose properties Examples: - Update all compose examples to use repository/revision instead of packageLocation/keyLocation Resolves: margo/specification#168, margo/specification#166, margo/specification#179 SUP: specification-enhancements/proposals/compose-oci/sup-01-compose-oci.md Breaking-Change: yes (pre-draft, no backward compat required) Signed-off-by: Andrii Melashchenko --- .../application-description.linkml.yaml | 26 +++-- .../invalid/ApplicationDescription-002.yaml | 4 +- .../valid/ApplicationDescription-002.yaml | 4 +- .../applications/resources/index.md.jinja2 | 10 +- .../desired-state.linkml.yaml | 13 ++- .../examples/valid/DesiredState-002.yaml | 4 +- .../examples/valid/gateway-autonomous.yaml | 4 +- .../examples/valid/gateway-directed.yaml | 4 +- .../applications/application-registry.md | 100 +++++++++++++++++- 9 files changed, 139 insertions(+), 30 deletions(-) diff --git a/src/specification/applications/application-description.linkml.yaml b/src/specification/applications/application-description.linkml.yaml index 4fe7a0c8..4ca7c264 100644 --- a/src/specification/applications/application-description.linkml.yaml +++ b/src/specification/applications/application-description.linkml.yaml @@ -394,13 +394,25 @@ classes: rank: 80 attributes: repository: - description: Repository location for the component. + description: >- + OCI repository URI for the component (e.g., oci://registry.example.com/org/component-name). + MUST be used for Helm, Compose, and Quadlet components. rank: 10 range: string + required: true revision: - description: Revision version for the component. - rank: 20 + description: >- + OCI tag identifying the component version within the repository + (e.g., "1.0.0", "2.3.1", "1.0.0-rc1", "1.0.0_build.123"). + MUST be a valid SemVer 2.0 string without a leading `v`. + SemVer build metadata separator `+` MUST be stored as `_` + because `+` is not a valid OCI tag character. + Implementations comparing this value against a SemVer string + MUST convert `_` back to `+` before comparison. range: string + required: true + rank: 20 + pattern: "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:_([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" wait: description: If True, indicates the device waits for the component installation to complete. rank: 30 @@ -409,14 +421,6 @@ classes: description: Time to wait for component installation to complete, formatted as "##m##s". rank: 40 range: string - packageLocation: - description: The URL indicating the Compose package's location. It should be a direct path to the compose.yaml or compose.yaml file archived in tar.gz - rank: 50 - range: string - keyLocation: - description: URL for the public key used to validate a digitally signed package. - rank: 60 - range: string Parameter: description: >- diff --git a/src/specification/applications/resources/examples/invalid/ApplicationDescription-002.yaml b/src/specification/applications/resources/examples/invalid/ApplicationDescription-002.yaml index 8dbbf496..747d9c54 100644 --- a/src/specification/applications/resources/examples/invalid/ApplicationDescription-002.yaml +++ b/src/specification/applications/resources/examples/invalid/ApplicationDescription-002.yaml @@ -59,8 +59,8 @@ deploymentProfiles: components: - name: digitron-orchestrator-docker properties: - packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz - keyLocation: https://northsitarida.com/digitron/docker/public-key.asc + repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator + revision: 1.0.9 parameters: idpName: targets: diff --git a/src/specification/applications/resources/examples/valid/ApplicationDescription-002.yaml b/src/specification/applications/resources/examples/valid/ApplicationDescription-002.yaml index c80a52ac..88f537f1 100644 --- a/src/specification/applications/resources/examples/valid/ApplicationDescription-002.yaml +++ b/src/specification/applications/resources/examples/valid/ApplicationDescription-002.yaml @@ -56,8 +56,8 @@ deploymentProfiles: components: - name: digitron-orchestrator-docker properties: - packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz - keyLocation: https://northsitarida.com/digitron/docker/public-key.asc + repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator + revision: 1.0.9 parameters: idpName: value: "test" diff --git a/src/specification/applications/resources/index.md.jinja2 b/src/specification/applications/resources/index.md.jinja2 index 7eb100a3..67892c4e 100644 --- a/src/specification/applications/resources/index.md.jinja2 +++ b/src/specification/applications/resources/index.md.jinja2 @@ -111,14 +111,12 @@ The expected properties for the suppported deployment types are indicated below. - Properties for `compose` components -> **Investigation Needed**: We need to have more discussion about how Compose should be handled and what is required here. - | Attribute | Type | Required? | Description | | --- | --- | --- | --- | -| packageLocation | string | Y | The URL indicating the Compose package's location. It should be a direct path to the compose.yaml or compose file archived in tar.gz | -| keyLocation | string | N | The public key used to validated the digitally signed package. It is highly recommend to digitally sign the package. When signing the package PGP MUST be used.| -| wait | bool | N | If `True`, indicates the device MUST wait until the Compose file has finished starting up before starting the next Compose file. The default is `True`. The Workload Fleet Management Client MUST support `True` and MAY support `False`. Only applies if multiple `compose` components are provided.| -| timeout | string | N | The time to wait for the component's installation to complete. If the installation does not completed before the timeout occurs the installation process fails. The format is "##m##s" indicating the total number of minutes and seconds to wait.| +| repository | string | Y | OCI repository URI for the Compose Archive (e.g., `oci://registry.example.com/org/component-name`). MUST be used for Compose components. See [Compose Component Registry](application-registry.md#compose-component-registry) for details.| +| revision | string | Y | OCI tag identifying the component version. MUST be a valid SemVer 2.0 string without a leading `v`. Build metadata separator `+` MUST be stored as `_` because `+` is not a valid OCI tag character.| +| wait | bool | N | If `True`, indicates the device MUST wait until all containers in the Compose project reach running state before reporting success. The default is `True`. The Workload Fleet Management Client MUST support `True` and MAY support `False`. Only applies if multiple `compose` components are provided.| +| timeout | string | N | The time to wait for the component's installation to complete. If the installation does not complete before the timeout occurs the installation process fails. The format is "##m##s" indicating the total number of minutes and seconds to wait.| ## Defining configurable application parameters diff --git a/src/specification/margo-management-interface/desired-state.linkml.yaml b/src/specification/margo-management-interface/desired-state.linkml.yaml index dd9ed4cb..c75309af 100644 --- a/src/specification/margo-management-interface/desired-state.linkml.yaml +++ b/src/specification/margo-management-interface/desired-state.linkml.yaml @@ -142,13 +142,22 @@ classes: Component: description: A class representing a component of a deployment profile. rank: 50 - # abstract: true attributes: name: description: The name of the component. - # identifier: true required: true rank: 10 + revision: + description: >- + OCI tag identifying the component version within the repository + (e.g., "1.0.0", "2.3.1", "1.0.0-rc1", "1.0.0_build.123"). + MUST be a valid SemVer 2.0 string without a leading `v`. + SemVer build metadata separator `+` MUST be stored as `_` + because `+` is not a valid OCI tag character. + range: string + required: false + rank: 15 + pattern: "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:_([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" properties: description: Properties associated with the component. range: Property diff --git a/src/specification/margo-management-interface/resources/examples/valid/DesiredState-002.yaml b/src/specification/margo-management-interface/resources/examples/valid/DesiredState-002.yaml index 5e555e0d..7cacc942 100644 --- a/src/specification/margo-management-interface/resources/examples/valid/DesiredState-002.yaml +++ b/src/specification/margo-management-interface/resources/examples/valid/DesiredState-002.yaml @@ -12,8 +12,8 @@ spec: components: - name: digitron-orchestrator-docker properties: - keyLocation: https://northsitarida.com/digitron/docker/public-key.asc - packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz + repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator + revision: 1.0.9 parameters: adminName: value: Some One diff --git a/src/specification/margo-management-interface/resources/examples/valid/gateway-autonomous.yaml b/src/specification/margo-management-interface/resources/examples/valid/gateway-autonomous.yaml index d15d4fd8..ced3f046 100644 --- a/src/specification/margo-management-interface/resources/examples/valid/gateway-autonomous.yaml +++ b/src/specification/margo-management-interface/resources/examples/valid/gateway-autonomous.yaml @@ -12,8 +12,8 @@ spec: components: - name: digitron-orchestrator-docker properties: - keyLocation: https://northsitarida.com/digitron/docker/public-key.asc - packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz + repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator + revision: 1.0.9 parameters: adminName: value: Some One diff --git a/src/specification/margo-management-interface/resources/examples/valid/gateway-directed.yaml b/src/specification/margo-management-interface/resources/examples/valid/gateway-directed.yaml index e15c54cd..445027b8 100644 --- a/src/specification/margo-management-interface/resources/examples/valid/gateway-directed.yaml +++ b/src/specification/margo-management-interface/resources/examples/valid/gateway-directed.yaml @@ -12,8 +12,8 @@ spec: components: - name: digitron-orchestrator-docker properties: - keyLocation: https://northsitarida.com/digitron/docker/public-key.asc - packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz + repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator + revision: 1.0.9 parameters: adminName: value: Some One diff --git a/system-design/specification/applications/application-registry.md b/system-design/specification/applications/application-registry.md index 3c2c0e7c..217dcf8d 100644 --- a/system-design/specification/applications/application-registry.md +++ b/system-design/specification/applications/application-registry.md @@ -229,6 +229,8 @@ The following response example is a Margo-specific OCI image manifest following |``application/vnd.margo.app.descriptionFile.v1+{file format}``| MUST be used to mark a layer in the OCI image manifest as pointing to description file of a Margo Application Package | |``application/vnd.margo.app.licenseFile.v1+{file format}``| MUST be used to mark a layer in the OCI image manifest as pointing to the license file of a Margo Application Package| |``application/vnd.margo.app.releaseNotes.v1+{file format}``| MUST be used to mark a layer in the OCI image manifest as pointing to the release notes file of a Margo Application Package| +|``application/vnd.org.margo.component.compose+json``| MUST be used as the **artifactType** in the OCI image manifest for a Margo Compose Archive | +|``application/vnd.org.margo.component.compose.tar+gzip``| MUST be used as the layer blob **mediaType** for a Margo Compose Archive | #### Margo-Specific Annotation Keys @@ -243,4 +245,100 @@ The following response example is a Margo-specific OCI image manifest following Retrieving the different files that compose an Application Package MUST be implemented according to the ["Pulling blobs" section of the OCI_spec](https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-blobs). -Also for this purpose available tools and libraries can be used for an implementation with low complexity. \ No newline at end of file +Also for this purpose available tools and libraries can be used for an implementation with low complexity. + +## Compose Component Registry + +Compose components MUST be stored in an OCI-compliant Component Registry and referenced via `repository` (an `oci://` URI) and `revision` (an OCI tag matching SemVer 2.0) in the ApplicationDescription and Desired State manifests. + +The OCI image manifest for a Compose component MUST use `artifactType` = `application/vnd.org.margo.component.compose+json`. The single layer blob MUST use `mediaType` = `application/vnd.org.margo.component.compose.tar+gzip`. + +### Compose Component Annotation Keys + +The following annotations MUST be set on the layer descriptor when pushing a Compose Archive to an OCI registry: + +| Annotation Key | Required? | Description | +|----------------|-----------|-------------| +| `org.margo.component.type` | REQUIRED | MUST be `compose`. Matches the `DeploymentProfile.type` enum value. | +| `org.margo.component.version` | REQUIRED | SemVer 2.0 version without leading `v`. MUST match the OCI tag and `ComponentProperties.revision`. | +| `org.opencontainers.image.title` | RECOMMENDED | Human-readable component name. | +| `org.opencontainers.image.authors` | RECOMMENDED | Author or organization (e.g., `ACME Corp (maintainer@example.com)`). | +| `org.opencontainers.image.description` | RECOMMENDED | Short description of the component. | +| `org.opencontainers.image.version` | RECOMMENDED | MUST equal `org.margo.component.version` if set. | + +These annotations enable registry UIs and tooling to discover and filter Margo artifacts without pulling the blob content. + +### Compose Archive Structure + +A Compose Archive is a gzip-compressed tar archive (`.tar.gz` or `.tgz`) that packages a Compose application for deployment on edge devices. The archive MUST conform to the following structural requirements. + +#### Directory Layout + +The archive MUST contain exactly one top-level directory. + +The directory name SHOULD match the component `name` as specified in the ApplicationDescription for human readability, but implementations MUST NOT depend on the directory name for discovery. + +Discovery algorithm: enter the single top-level directory; locate the file named `compose.yaml`. + +The top-level directory MAY contain any number of subdirectories (e.g., `configs/`, `certs/`, `scripts/`). All referenced files MUST resolve within the top-level directory. + +The top-level directory MUST contain a file named `compose.yaml`. The Compose file MUST conform to the Compose Specification as currently published. + +> **Note:** The Compose file MUST be named `compose.yaml`. The alternative names `compose.yml`, `docker-compose.yaml`, and `docker-compose.yml` are NOT valid within a Margo Compose Archive. + +Files referenced by `compose.yaml` via `env_file` entries and `configs` (file source) MUST be included within the archive and MUST be referenced using relative paths that resolve within the top-level directory. + +Bind-mount volume paths declared in `volumes` are runtime paths and MUST NOT be included in the archive. + +Files for `secrets` (file source) MUST NOT be included in the archive. Secret provisioning is out of scope and is the responsibility of the device or WFM implementation at deployment time. + +#### Security Constraints + +- Symlinks MUST NOT target paths outside the top-level directory. +- Hard links MUST NOT reference paths outside the top-level directory. +- Absolute paths MUST NOT appear in the archive entries. +- File names MUST NOT contain path traversal sequences (`../`). +- Implementations SHOULD normalize file permissions during archive extraction. Implementations MUST NOT preserve setuid, setgid, or sticky bits from archive entries. +- WFM and device implementations MUST validate these constraints before extracting or deploying the archive. + +#### Integrity Verification + +When stored in an OCI-compliant Component Registry, the Compose Archive tarball is the content of a single layer blob. Integrity verification at the transport layer is provided by the OCI content-addressable digest as mandated by the [OCI Distribution Specification v1.1.0](https://github.com/opencontainers/distribution-spec/blob/v1.1.0/spec.md). Implementations MUST verify the OCI digest after pulling the blob and before extracting the archive. + +### Publishing Workflow + +To publish a Compose Archive to an OCI-compliant Component Registry, use `oras push` ([ORAS — OCI Registry as Storage](https://oras.land/)). This is the RECOMMENDED publishing tool for Margo Compose Archives. + +> **Warning**: `docker compose publish` (Docker Compose 2.34.0+) MUST NOT be used to publish Margo Compose components. It produces a structurally incompatible OCI artifact: `artifactType: application/vnd.docker.compose.project`, multiple layers (one per file), and SHA256-hashed file paths. This format cannot be consumed by a Margo-compliant WFM or device implementation. + +Example: + +```bash +oras push registry.example.com/org/myapp:1.0.0 \ + --artifact-type application/vnd.org.margo.component.compose+json \ + myapp-1.0.0-compose.tar.gz:application/vnd.org.margo.component.compose.tar+gzip \ + --annotation "org.margo.component.type=compose" \ + --annotation "org.margo.component.version=1.0.0" \ + --annotation "org.opencontainers.image.title=myapp" \ + --annotation "org.opencontainers.image.version=1.0.0" \ + --annotation "org.opencontainers.image.authors=ACME Corp (maintainer@example.com)" \ + --annotation "org.opencontainers.image.description=My application workload" +``` + +Reference the artifact in the ApplicationDescription: + +```yaml +components: + - name: myapp + properties: + repository: oci://registry.example.com/org/myapp + revision: "1.0.0" +``` + +### WFM Reconciliation and `wait` Semantics for Compose + +If `wait` is set to `true` for a Compose component, the device MUST wait until all containers in the Compose project reach **running** state before reporting the deployment as successful. This is equivalent to `docker compose up` or `podman-compose up` completing synchronously without `--detach`. + +If any container exits with a non-zero exit code during startup, the deployment MUST be reported as failed immediately. + +If health checks are defined in `compose.yaml`, implementations SHOULD additionally wait for all containers to reach **healthy** state before reporting success. \ No newline at end of file From 35ccd36abf1cf802d973bc584b443969a3b5935b Mon Sep 17 00:00:00 2001 From: Andrii Melashchenko <6060861+javatask@users.noreply.github.com> Date: Thu, 30 Jul 2026 19:51:24 +0200 Subject: [PATCH 2/2] refactor(schema): unify Component/Properties, remove type subclasses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address PR #200 review feedback (phil-abb, sulksamino): - Remove HelmComponent, ComposeComponent (empty subclasses) - Remove HelmDeploymentProfile, ComposeDeploymentProfile (use pattern) - Remove duplicate Component.revision (keep in ComponentProperties only) - Replace Property key/value bag with typed ComponentProperties - Add pattern constraints: oci:// URI, SemVer regex, timeout format - Unify docs: single ComponentProperties table for all deployment types - Fix DesiredState-001.yaml: wait: "true" (string) → wait: true (bool) - Clarify ORAS is example tool, not requirement - Remove WFM from reconciliation subheader Discriminator is now DeploymentProfile.type (pattern: ^(helm|compose)$). Adding new types (e.g. quadlet) requires only a regex update. --- .../application-description.linkml.yaml | 57 ++++------- .../applications/resources/index.md.jinja2 | 21 +--- .../desired-state.linkml.yaml | 96 +++++++++---------- .../examples/valid/DesiredState-001.yaml | 4 +- .../applications/application-registry.md | 4 +- 5 files changed, 72 insertions(+), 110 deletions(-) diff --git a/src/specification/applications/application-description.linkml.yaml b/src/specification/applications/application-description.linkml.yaml index 4ca7c264..161f2b1e 100644 --- a/src/specification/applications/application-description.linkml.yaml +++ b/src/specification/applications/application-description.linkml.yaml @@ -337,28 +337,6 @@ classes: range: CommunicationInterfaceType required: true - HelmDeploymentProfile: - is_a: DeploymentProfile - #rank: 63 - slot_usage: - type: - equals_string: "helm" - rank: 10 - components: - range: HelmComponent - rank: 20 - - ComposeDeploymentProfile: - is_a: DeploymentProfile - #rank: 66 - slot_usage: - type: - equals_string: "compose" - rank: 10 - components: - range: ComposeComponent - rank: 20 - Component: description: A class representing a component of a deployment profile. rank: 70 @@ -381,25 +359,22 @@ classes: slots: - x-placeholder-extensions - HelmComponent: - is_a: Component - #rank: 73 - - ComposeComponent: - is_a: Component - #rank: 76 - ComponentProperties: - description: Properties dictionary for component deployment details. + description: >- + Typed component properties providing OCI coordinates + and optional deployment behavior settings. + Used by both Helm and Compose deployment types. rank: 80 attributes: repository: description: >- - OCI repository URI for the component (e.g., oci://registry.example.com/org/component-name). - MUST be used for Helm, Compose, and Quadlet components. + OCI repository URI for the component package + (e.g., oci://registry.example.com/org/component-name). + MUST be a valid OCI reference using the oci:// scheme. rank: 10 range: string required: true + pattern: "^oci://.+" revision: description: >- OCI tag identifying the component version within the repository @@ -407,20 +382,26 @@ classes: MUST be a valid SemVer 2.0 string without a leading `v`. SemVer build metadata separator `+` MUST be stored as `_` because `+` is not a valid OCI tag character. - Implementations comparing this value against a SemVer string - MUST convert `_` back to `+` before comparison. + rank: 20 range: string required: true - rank: 20 pattern: "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:_([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" wait: - description: If True, indicates the device waits for the component installation to complete. + description: >- + Whether to wait for component installation to complete before proceeding. + For Helm: waits until the release is deployed. + For Compose: waits until all containers reach running state. + Default is true. Only applies when multiple components are provided. rank: 30 range: boolean timeout: - description: Time to wait for component installation to complete, formatted as "##m##s". + description: >- + Maximum time to wait for component installation to complete. + Format is "##m##s" (e.g., "5m0s", "8m30s"). + If installation does not complete before timeout, deployment fails. rank: 40 range: string + pattern: "^\\d+m\\d+s$" Parameter: description: >- diff --git a/src/specification/applications/resources/index.md.jinja2 b/src/specification/applications/resources/index.md.jinja2 index 67892c4e..92f11b75 100644 --- a/src/specification/applications/resources/index.md.jinja2 +++ b/src/specification/applications/resources/index.md.jinja2 @@ -48,7 +48,7 @@ which defines the [desired state](../margo-management-interface/desired-state.md {% endfor -%} {% for c in gen.all_class_objects()|sort(attribute='rank') %} -{%- if c.name != "ApplicationDescription" and not c.name.startswith("ComponentProperties") and not c.name.startswith("Helm") and not c.name.startswith("Compose") %} +{%- if c.name != "ApplicationDescription" and not c.name.startswith("ComponentProperties") %} {# if c is a subclass of any: #} @@ -98,25 +98,14 @@ Your Helm based application MUST NOT rely on the following Helm functions: {%- if c.name.startswith("ComponentProperties") %} ### ComponentProperties Attributes -The expected properties for the suppported deployment types are indicated below. - -- Properties for `helm` components - -| Attribute | Type | Required? | Description | -| --- | --- | --- | --- | -| repository | string | Y | The URL indicating the helm chart's location.| -| revision | string | Y | The helm chart's full version.| -| wait | bool | N | If `True`, indicates the device MUST wait until the helm chart has finished installing before installing the next helm chart. The default is `True`. The Workload Fleet Management Client MUST support `True` and MAY support `False`. Only applies if multiple `helm` components are provided.| -| timeout | string | N | The time to wait for the component's installation to complete. If the installation does not completed before the timeout occurs the installation process fails. The format is "##m##s" indicating the total number of minutes and seconds to wait. | - -- Properties for `compose` components +The expected properties for all OCI-based deployment types (Helm and Compose) are indicated below. | Attribute | Type | Required? | Description | | --- | --- | --- | --- | -| repository | string | Y | OCI repository URI for the Compose Archive (e.g., `oci://registry.example.com/org/component-name`). MUST be used for Compose components. See [Compose Component Registry](application-registry.md#compose-component-registry) for details.| +| repository | string | Y | OCI repository URI for the component package (e.g., `oci://registry.example.com/org/component-name`). MUST be a valid OCI reference using the `oci://` scheme. See [Compose Component Registry](application-registry.md#compose-component-registry) for details.| | revision | string | Y | OCI tag identifying the component version. MUST be a valid SemVer 2.0 string without a leading `v`. Build metadata separator `+` MUST be stored as `_` because `+` is not a valid OCI tag character.| -| wait | bool | N | If `True`, indicates the device MUST wait until all containers in the Compose project reach running state before reporting success. The default is `True`. The Workload Fleet Management Client MUST support `True` and MAY support `False`. Only applies if multiple `compose` components are provided.| -| timeout | string | N | The time to wait for the component's installation to complete. If the installation does not complete before the timeout occurs the installation process fails. The format is "##m##s" indicating the total number of minutes and seconds to wait.| +| wait | bool | N | If `True`, indicates the device MUST wait until the component installation completes before installing the next component. For Helm: waits until the release is deployed. For Compose: waits until all containers reach running state. The default is `True`. The Workload Fleet Management Client MUST support `True` and MAY support `False`. Only applies if multiple components are provided.| +| timeout | string | N | The time to wait for the component's installation to complete. If the installation does not complete before the timeout occurs the installation process fails. The format is "##m##s" indicating the total number of minutes and seconds to wait. | ## Defining configurable application parameters diff --git a/src/specification/margo-management-interface/desired-state.linkml.yaml b/src/specification/margo-management-interface/desired-state.linkml.yaml index c75309af..9c04ec18 100644 --- a/src/specification/margo-management-interface/desired-state.linkml.yaml +++ b/src/specification/margo-management-interface/desired-state.linkml.yaml @@ -111,34 +111,11 @@ classes: DeploymentProfile: description: A class representing a deployment profile. rank: 40 - # abstract: true slots: - type - components - x-placeholder-extensions - HelmDeploymentProfile: - is_a: DeploymentProfile - rank: 40 - slot_usage: - type: - equals_string: "helm" - rank: 10 - components: - range: HelmComponent - rank: 20 - - ComposeDeploymentProfile: - is_a: DeploymentProfile - rank: 40 - slot_usage: - type: - equals_string: "compose" - rank: 10 - components: - range: ComposeComponent - rank: 20 - Component: description: A class representing a component of a deployment profile. rank: 50 @@ -147,47 +124,62 @@ classes: description: The name of the component. required: true rank: 10 - revision: - description: >- - OCI tag identifying the component version within the repository - (e.g., "1.0.0", "2.3.1", "1.0.0-rc1", "1.0.0_build.123"). - MUST be a valid SemVer 2.0 string without a leading `v`. - SemVer build metadata separator `+` MUST be stored as `_` - because `+` is not a valid OCI tag character. - range: string - required: false - rank: 15 - pattern: "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:_([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" properties: - description: Properties associated with the component. - range: Property + description: >- + Component deployment properties providing OCI coordinates and behavior settings. + See the ComponentProperties section below. + range: ComponentProperties required: true - multivalued: true inlined: true - inlined_as_list: false rank: 20 slots: - x-placeholder-extensions - HelmComponent: - is_a: Component - rank: 50 - - ComposeComponent: - is_a: Component - rank: 50 - - Property: - rank: 80 + ComponentProperties: + rank: 82 + description: >- + Typed component properties providing OCI coordinates + and optional deployment behavior settings. + Used by both Helm and Compose deployment types. attributes: - name: - identifier: true + repository: + description: >- + OCI repository URI for the component package + (e.g., oci://registry.example.com/org/component-name). + MUST be a valid OCI reference using the oci:// scheme. range: string + required: true + pattern: "^oci://.+" rank: 10 - value: + revision: + description: >- + OCI tag identifying the component version within the repository + (e.g., "1.0.0", "2.3.1", "1.0.0-rc1", "1.0.0_build.123"). + MUST be a valid SemVer 2.0 string without a leading `v`. + SemVer build metadata separator `+` MUST be stored as `_` + because `+` is not a valid OCI tag character. range: string - rank: 20 required: true + pattern: "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:_([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + rank: 20 + wait: + description: >- + Whether to wait for component installation to complete before proceeding. + For Helm: waits until the release is deployed. + For Compose: waits until all containers reach running state. + Default is true. Only applies when multiple components are provided. + range: boolean + required: false + rank: 30 + timeout: + description: >- + Maximum time to wait for component installation to complete. + Format is "##m##s" (e.g., "5m0s", "8m30s"). + If installation does not complete before timeout, deployment fails. + range: string + required: false + pattern: "^\\d+m\\d+s$" + rank: 40 Parameter: description: A class representing a single parameter. diff --git a/src/specification/margo-management-interface/resources/examples/valid/DesiredState-001.yaml b/src/specification/margo-management-interface/resources/examples/valid/DesiredState-001.yaml index 581085cb..73406478 100644 --- a/src/specification/margo-management-interface/resources/examples/valid/DesiredState-001.yaml +++ b/src/specification/margo-management-interface/resources/examples/valid/DesiredState-001.yaml @@ -15,12 +15,12 @@ spec: repository: oci://quay.io/charts/realtime-database-services revision: 2.3.7 timeout: 8m30s - wait: "true" + wait: true - name: digitron-orchestrator properties: repository: oci://northstarida.azurecr.io/charts/northstarida-digitron-orchestrator revision: 1.0.9 - wait: "true" + wait: true parameters: adminName: value: Some One diff --git a/system-design/specification/applications/application-registry.md b/system-design/specification/applications/application-registry.md index 217dcf8d..74993c25 100644 --- a/system-design/specification/applications/application-registry.md +++ b/system-design/specification/applications/application-registry.md @@ -307,7 +307,7 @@ When stored in an OCI-compliant Component Registry, the Compose Archive tarball ### Publishing Workflow -To publish a Compose Archive to an OCI-compliant Component Registry, use `oras push` ([ORAS — OCI Registry as Storage](https://oras.land/)). This is the RECOMMENDED publishing tool for Margo Compose Archives. +To publish a Compose Archive to an OCI-compliant Component Registry, implementations MAY use any OCI-compliant push tool. The example below uses `oras push` ([ORAS — OCI Registry as Storage](https://oras.land/)), which is a RECOMMENDED tool for Margo Compose Archives. > **Warning**: `docker compose publish` (Docker Compose 2.34.0+) MUST NOT be used to publish Margo Compose components. It produces a structurally incompatible OCI artifact: `artifactType: application/vnd.docker.compose.project`, multiple layers (one per file), and SHA256-hashed file paths. This format cannot be consumed by a Margo-compliant WFM or device implementation. @@ -335,7 +335,7 @@ components: revision: "1.0.0" ``` -### WFM Reconciliation and `wait` Semantics for Compose +### Reconciliation and `wait` Semantics for Compose If `wait` is set to `true` for a Compose component, the device MUST wait until all containers in the Compose project reach **running** state before reporting the deployment as successful. This is equivalent to `docker compose up` or `podman-compose up` completing synchronously without `--detach`.