Getting Started with OCM

This chapter walks you through some basic steps to get started with OCM concepts and the OCM CLI.

Prerequisites

To follow the steps described in this section, you will need:

  • The OCM Command Line Interface (CLI) to interact with component versions and registries. Download it from the releases or with the following command:

    #generic: use a bash installer
    curl -s https://ocm.software/install.sh | sudo bash
    
    #macos: use homebrew
    brew install open-component-model/tap/ocm
    
  • Access to an OCM repository. This can be any OCI registry for which you have write permission (e.g. GitHub Packages). An OCM repository based on an OCI registry is identified by a leading OCI repository prefix. For example: ghcr.io/<YOUR-ORG>/ocm.

  • Credentials for the CLI to access the OCM repository. The easiest way to do this is to reuse the Docker configuration.

    To do this, create a file named .ocmconfig in your home directory with the following:

    type: generic.config.ocm.software/v1
    configurations:
    - type: credentials.config.ocm.software
      repositories:
        - repository:
            type: DockerConfig/v1
            dockerConfigFile: "~/.docker/config.json"
            propagateConsumerIdentity: true
    - type: attributes.config.ocm.software
      attributes:
        cache: ~/.ocm/cache
    

Create a component version

The first step when creating a new component version is to create a component archive. A component archive contains references, resources and sources. The ocm CLI tool can help with this.

For convenience, we define the following environment variables:

PROVIDER="acme.org"
ORG="acme"
COMPONENT="github.com/${ORG}/helloworld"
VERSION="1.0.0"
CA_ARCHIVE="ca-hello-world"
OCM_REPO="ghcr.io/<github-org>/ocm"

If you specify values for your setup, you can directly use the commands shown in the next steps. The variable OCM_REPO is set to a location of an OCI registry where artifacts and component descriptors are stored (omitting the https:// prefix). For example GitHub Packages can be used as an OCI registry. Many other options exist.

Let’s asssume that we create a component based on a GitHub source repository.

Create a component archive

First we create an empty component archive using the command ocm create componentarchive:

ocm create componentarchive ${COMPONENT} ${VERSION}  --provider ${PROVIDER} --file $CA_ARCHIVE
What happened?

This command creates the following file structure:

$ tree ca-hello-world
ca-hello-world
├── blobs
└── component-descriptor.yaml

The component descriptor is stored as a yaml file named component-descriptor.yaml. It describes the content of a component version.

It contains the following configuration:

meta:
  schemaVersion: v2
component:
  name: github.com/acme/helloworld
  version: 1.0.0
  provider: acme.org
  resources: []
  sources: []
  componentReferences: []

By default, the command creates a directory structure. The option --type can be used to select other target formats, such as tar or tgz.

Add a local resource

The next step is ocm add resources. First, we want to add a Helm Chart stored in a local folder named helmchart.

ocm add resource $CA_ARCHIVE --type helmChart --name mychart --version ${VERSION} --inputType helm --inputPath ./helmchart
processing resource (by options)...
  processing document 1...
    processing index 1
found 1 resources
adding resource helmChart: "name"="mychart","version"="1.0.0"...
What happened?

The generated file structure is:

tree ca-hello-world
ca-hello-world
├── blobs
│   └── sha256.60bfd05083f81f2841657e24410d3ba25e4bcc3e3c927da7e1811e775116a74d
└── component-descriptor.yaml

The added blob contains the packaged Helm Chart. The blob is referenced in the component descriptor in component.resources.access.localreference:

meta:
  schemaVersion: v2
component:
  name: github.com/acme/helloworld
  version: 1.0.0
  provider: acme.org
  resources:
  - access:
      localReference: sha256.60bfd05083f81f2841657e24410d3ba25e4bcc3e3c927da7e1811e775116a74d
      mediaType: application/vnd.oci.image.manifest.v1+tar+gzip
      referenceName: github.com/acme/helloworld/echoserver:0.1.0
      type: localBlob
    name: mychart
    relation: local
    type: helmChart
    version: 1.0.0
  sources: []
  componentReferences: []

Because we use content from the local environment, it is directly packaged into the component archive using the access method of type local.

Add an image reference

Next, we will add an image. The image is already stored in an image registry (e.g. by a previous Docker build/push).

ocm add resource $CA_ARCHIVE --type ociImage --name image --version ${VERSION} --accessType ociArtifact --reference gcr.io/google_containers/echoserver:1.10
processing resource (by options)...
  processing document 1...
    processing index 1
found 1 resources
adding resource ociImage: "name"="image","version"="1.0.0"...
What happened? The component descriptor now has the following content, with an additional `access` under `component.resources`, where `access` is of type `external`:
meta:
  schemaVersion: v2
component:
  name: github.com/acme/helloworld
  version: 1.0.0
  provider: acme.org
  resources:
  - access:
      localReference: sha256.60bfd05083f81f2841657e24410d3ba25e4bcc3e3c927da7e1811e775116a74d
      mediaType: application/vnd.oci.image.manifest.v1+tar+gzip
      referenceName: github.com/acme/helloworld/echoserver:0.1.0
      type: localBlob
    name: mychart
    relation: local
    type: helmChart
    version: 1.0.0
  - access:
      imageReference: gcr.io/google_containers/echoserver:1.10
      type: ociArtifact
    name: image
    relation: external
    type: ociImage
    version: 1.0.0
  sources: []
  componentReferences: []
  repositoryContexts: []

Add an image resource

Alternatively you can add an image as a resource built locally using Docker before. It will be picked up from the local Docker file system and added to the component archive.

docker image ls

REPOSITORY                                              TAG                 IMAGE ID       CREATED         SIZE
echoserverimage                                         1.10.0              365ec60129c5   4 years ago     95.4MB
...
ocm add resource ${CA_ARCHIVE} --name image --version ${VERSION} --type ociArtifact  --inputType docker --inputPath=echoserverimage:1.10.0
processing resource (by options)...
  processing document 1...
    processing index 1
found 1 resources
adding resource ociArtifact: "name"="image","version"="1.0.0"...
  image echoserverimage:1.10.0
locator: echoserverimage, repo: , version 1.10.0
~/temp/ocm/hello>
What happened? The Docker image is downloaded from the Docker daemon, converted to an OCI artifact, and added as local artifact to the component version. The component descriptor now has the content:
component:
  componentReferences: []
  name: github.com/acme/helloworld
  provider: acme.org
  repositoryContexts: []
  resources:
  - access:
      localReference: sha256.31b7c98008a6b2d6f0b07e11b997c47c70d31edeea0986382940ccc42288741e
      mediaType: application/vnd.oci.image.manifest.v1+tar+gzip
      referenceName: github.com/acme/helloworld/echoserverimage:1.10.0
      type: localBlob
    name: image
    relation: local
    type: ociArtifact
    version: 1.0.0
  - access:
      localReference: sha256.cf0b6e95c1f05e6825d37e15744f46c6fdb37eea7be2234b8948be71b28ff6b1
      mediaType: application/vnd.oci.image.manifest.v1+tar+gzip
      referenceName: github.com/acme/helloworld/echoserver:0.1.0
      type: localBlob
    name: mychart
    relation: local
    type: helmChart
    version: 1.0.0
  sources: []
  version: 1.0.0
meta:
  schemaVersion: v2

The generated blob sha256.65cf… is an archive describing the image according to the OCI Image Layout Specification.

Using a resources file

You could simplify the previous two steps (adding helm chart and image as resources) by using a text file as input. For that, you could create a file resources.yaml, which looks like this:

---
name: mychart
type: helmChart
input:
  type: helm
  path: ./helmchart
---
name: image
type: ociImage
version: "1.0.0"
access:
  type: ociArtifact
  imageReference: gcr.io/google_containers/echoserver:1.10

A resource is described either by its accesss information to a remote repository or by locally provided resources. For remote access the field access is used to describe the access method. The type field is used to specify the kind of access. If the resource content should be taken from local resources with the field input is used to specify the access to local resources. In this case the resource content is directly put into the component archive. Like for the access attribute the kind of the input source is described by the field type. The input types are not part of the input specification but are provided locally by the OCM command line client. For available input types see ocm add resources.

For more complex scenarios the description files might use variable substitution (templating), see Best practices

Add the resources using the following command:

ocm add resources $CA_ARCHIVE resources.yaml
processing resources.yaml...
  processing document 1...
    processing index 1
  processing document 2...
    processing index 1
found 2 resources
adding resource helmChart: "name"="mychart","version"="<componentversion>"...
adding resource ociImage: "name"="image","version"="1.0.0"...

For a local image built with Docker use this file:

---
name: mychart
type: helmChart
input:
  type: helm
  path: ./helmchart
---
name: image
type: ociImage
version: "1.0.0"
input:
  type: docker
  repository: echoserverimage
  path: echoserverimage:1.10.0

Uploading component versions

To upload the component version to an OCI registry, you can transfer the component archive using the command ocm transfer componentarchive:

OCMREPO=ghcr.io/acme
ocm transfer componentarchive ./ca-hello-world ${OCMREPO}
transferring version "github.com/acme/helloworld:1.0.0"...
...resource 0(github.com/acme/helloworld/echoserver:0.1.0)...
...adding component version...

Bundle composed components

If you have created multiple components according to the instructions above, you can bundle them into a single archive entity. This can be done by creating a transport archive.

The transport archive is the entity that does the transfer between component repositories. It is used to transfer entire deployments between locations.

A transport archive may contain any number of component versions. It may also be pushed to an OCM repository.

Note that a transport achive is also an OCM repository, so it can also be used as source or as a target for transport operations.

CTF_ARCHIVE=ctf-hello-world
ocm transfer componentversion ${CA_ARCHIVE} ${CTF_ARCHIVE}
transferring version "github.com/acme/helloworld:1.0.0"...
...resource 0(github.com/acme/helloworld/echoserver:0.1.0)...
...adding component version...
1 versions transferred
What happened?

The resulting transport archive has the following file structure:

tree ${CTF_ARCHIVE}
ctf-hello-world
├── artifact-index.json
└── blobs
    ├── sha256.378a171e7a1bcecc19b7fd4a330161a9d91550486dad668c78d08e590ef245e7
    ├── sha256.4f2080d8d41d2b52182f325f4f42d91e2581e3f2299f4f8631196801773ba869
    ├── sha256.63dc40246a604ef503f0361e14216ab7e002912697d09da49f50bba7091549f7
    └── sha256.b9bf66cb07b129d12956392dff6110874c37a1b06ed8dde88881f6de971ff293

The transport archive’s contents can be found in artifact-index.json. This file contains the list of component version artifacts to be transported.

jq . ${CTF_ARCHIVE}/artifact-index.json
{
  "schemaVersion": 1,
  "artifacts": [
    {
      "repository": "component-descriptors/github.com/acme/helloworld",
      "tag": "1.0.0",
      "digest": "sha256:63dc40246a604ef503f0361e14216ab7e002912697d09da49f50bba7091549f7"
    }
  ]
}

The content of the transport archive is stored as OCI artifacts. Notice that the repository names of Component Version artifacts (found at artifacts.respository) are prefixed by component-descriptors/.

The component version is described as an OCI manifest:

jq . ${CTF_ARCHIVE}/blobs/sha256.63dc40246a604ef503f0361e14216ab7e002912697d09da49f50bba7091549f7
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.ocm.software.component.config.v1+json",
    "digest": "sha256:b9bf66cb07b129d12956392dff6110874c37a1b06ed8dde88881f6de971ff293",
    "size": 201
  },
  "layers": [
    {
      "mediaType": "application/vnd.ocm.software.component-descriptor.v2+yaml+tar",
      "digest": "sha256:4f2080d8d41d2b52182f325f4f42d91e2581e3f2299f4f8631196801773ba869",
      "size": 2560
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+tar+gzip",
      "digest": "sha256:378a171e7a1bcecc19b7fd4a330161a9d91550486dad668c78d08e590ef245e7",
      "size": 4747
    }
  ]
}

Notice that the output of the component version above contains the component descriptor as one of the layers. It can be identified by its content type, which is application/vnd.ocm.software.component-descriptor.v2+yaml+tar. In ths case, the component descriptor can be displayed with the following command:

tar xvf ctf-hello-world/blobs/sha256.4f2080d8d41d2b52182f325f4f42d91e2581e3f2299f4f8631196801773ba869 -O - component-descriptor.yaml
component:
  componentReferences: []
  name: github.com/acme/helloworld
  provider: acme.org
  repositoryContexts: []
  resources:
  - access:
      localReference: sha256:378a171e7a1bcecc19b7fd4a330161a9d91550486dad668c78d08e590ef245e7
      mediaType: application/vnd.oci.image.manifest.v1+tar+gzip
      referenceName: github.com/acme/helloworld/echoserver:0.1.0
      type: localBlob
    name: mychart
    relation: local
    type: helmChart
    version: 1.0.0
  - access:
      imageReference: gcr.io/google_containers/echoserver:1.10
      type: ociArtifact
    name: image
    relation: external
    type: ociImage
    version: 1.0.0
  sources: []
  version: 1.0.0
meta:
  schemaVersion: v2

The other elements listed as layers describe the blobs for the local resources stored along with the component version. The digests can be seen in the localReference attributes of the component descriptor.

All in One

The previous steps can be combined into a single operation working on a single description file:

  • Creating a Common Transport Archive
  • Adding one or more components
  • With resources, sources and references

The command ocm add componentversions directly creates or extends a common transport archive without the need for creating dedicated component archives

Create a yaml configuration file components.yaml, which contains information about the components to create and the elements addded to those components

components:
- name: github.com/acme.org/helloworld
  version: "1.0.0"
  provider:
    name: acme.org
  resources:
    - name: mychart
      type: helmChart
      input:
        type: helm
        path: ./helmchart
    - name: image
      type: ociImage
      version: "1.0.0"
      access:
        type: ociArtifact
        imageReference: gcr.io/google_containers/echoserver:1.10
ocm add componentversions --create --file ${CTF_ARCHIVE} components.yaml
processing components.yaml...
  processing document 1...
    processing index 1
found 1 component
adding component github.com/acme.org/helloworld:1.0.0...
  adding resource helmChart: "name"="mychart","version"="<componentversion>"...
  adding resource ociImage: "name"="image","version"="1.0.0"...
What happened? The command creates the common-transport-archive (option `--create`), and adds the listed components with the described resources.
tree ${CTF_ARCHIVE}
ctf-hello-world
├── artifact-index.json
└── blobs
    ├── sha256.35ab56695654b260c633453d59064ad2aa075fadf1811b6712f982ea4f3cf814
    ├── sha256.3732cc9004408e98ec28b66c1844dff166294a5bb03f953220d1542fba10de92
    ├── sha256.d275a99002962136691f3982b7e176a2812a22193809aa2c879e29cac6851919
    └── sha256.ee6d6431f54c511015a59203213c998ba0654730a3f3279b56d1b29e9b51b068

Display and Examine component versions

List component versions

To show the component stored in a component archive (without looking at the file system structure), the ocm get componentversion command can be used:

ocm get componentversion ${CA_ARCHIVE}
COMPONENT                  VERSION PROVIDER
github.com/acme/helloworld 1.0.0   acme.org

To see the component descriptor of the displayed component version, use the output format option -o yaml:

ocm get componentversion ${CA_ARCHIVE} -o yaml
---
context: []
element:
  component:
    componentReferences: []
    name: github.com/acme/helloworld
    provider:
      name: acme.org
    repositoryContexts: []
    resources: []
    sources: []
    version: 1.0.0
  meta:
    configuredSchemaVersion: v2

Display the component versions of any OCM repository with this command:

ocm get cv ghcr.io/mandelsoft/cnudie//github.com/mandelsoft/ocmhelmdemo
COMPONENT                         VERSION   PROVIDER
github.com/mandelsoft/ocmhelmdemo 0.1.0-dev mandelsoft

To refer to the content of a component repository, the component name can be appended to the repository specification separated by //.

In the example above, ghcr.io/mandelsoft/cnudie is the OCM repository, whereas github.com/mandelsoft/ocmhelmdemo is the component stored in this component repository.

Optionally, a specific version can be appended, separated by a colon (:). If no version is specified, all component versions will be displayed.

With the option --recursive, it is possible to show the complete component version, including the component versions it references.

ocm get cv ghcr.io/mandelsoft/cnudie//github.com/mandelsoft/ocmhelmdemo --recursive
REFERENCEPATH                               COMPONENT                              VERSION   PROVIDER   IDENTITY
                                            github.com/mandelsoft/ocmhelmdemo      0.1.0-dev mandelsoft
github.com/mandelsoft/ocmhelmdemo:0.1.0-dev github.com/mandelsoft/ocmhelminstaller 0.1.0-dev mandelsoft "name"="installer"

To get a tree view, add the option -o tree:

ocm get componentversion ghcr.io/mandelsoft/cnudie//github.com/mandelsoft/ocmhelmdemo --recursive -o tree
NESTING    COMPONENT                              VERSION   PROVIDER   IDENTITY
└─ ⊗       github.com/mandelsoft/ocmhelmdemo      0.1.0-dev mandelsoft
   └─      github.com/mandelsoft/ocmhelminstaller 0.1.0-dev mandelsoft "name"="installer"

List the resources of a component version

To list the resources found in a component version tree, the command ocm get resources can be used:

ocm get resources ghcr.io/mandelsoft/cnudie//github.com/mandelsoft/ocmhelmdemo:0.1.0-dev --recursive -o tree
COMPONENT                                    NAME        VERSION   IDENTITY TYPE        RELATION
└─ github.com/mandelsoft/ocmhelmdemo                     0.1.0-dev
   ├─                                        chart       0.1.0-dev          helmChart   local
   ├─                                        image       1.0                ociImage    external
   ├─                                        package     0.1.0-dev          toiPackage  local
   └─ github.com/mandelsoft/ocmhelminstaller installer   0.1.0-dev
      ├─                                     toiexecutor 0.1.0-dev          toiExecutor local
      └─                                     toiimage    0.1.0-dev          ociImage    local

Download the resources of a component version

Use the ocm download command to download resources such as component versions, individual resources or artifacts:

ocm download resource ghcr.io/jensh007//github.com/acme/helloworld:1.0.0 chart -O helmchart.tgz
helmchart.tgz: 4747 byte(s) written

Because it is stored as OCI artifact in an OCI registry, the filesystem format used for OCI artifacts is the blob format.

What happened? The file helmchart.tgz was downloaded.
tar xvf helmchart.tgz
x index.json
x oci-layout
x blobs
x blobs/sha256.1c1af427d477202d102c141f27d3be0f5b6595e2948a82ec58987560c1915fea
x blobs/sha256.47eacca4cbed4b63c17e044d3c87a33d9bd1f88a9e76fa0ab051e48b0a3cd7ec
x blobs/sha256.ea8e5b44cd1aff1f3d9377d169ad795be20fbfcd58475a62341ed8fb74d4788c
$ jq . index.json
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:47eacca4cbed4b63c17e044d3c87a33d9bd1f88a9e76fa0ab051e48b0a3cd7ec",
      "size": 410,
      "annotations": {
        "cloud.gardener.ocm/tags": "0.1.0",
        "org.opencontainers.image.ref.name": "0.1.0",
        "software.ocm/tags": "0.1.0"
      }
    }
  ],
  "annotations": {
    "cloud.gardener.ocm/main": "sha256:47eacca4cbed4b63c17e044d3c87a33d9bd1f88a9e76fa0ab051e48b0a3cd7ec",
    "software.ocm/main": "sha256:47eacca4cbed4b63c17e044d3c87a33d9bd1f88a9e76fa0ab051e48b0a3cd7ec"
  }
}

Download with download handlers

To use a format more suitable for the content technology, enable the usage of download handlers.

If a download handler is available for the artifact type and the blob media type used to store the blob in the OCM repository, it will convert the blob format into a more suitable format:

ocm download resource -d ghcr.io/jensh007//github.com/acme/helloworld:1.0.0 chart -O helmchart.tgz
helmchart.tgz: 4747 byte(s) written
What happened? The downloaded archive is now a regular Helm Chart archive:
tar tvf echoserver-0.1.0.tgz
-rw-r--r--  0 0      0         136 Nov 30 13:19 echoserver/Chart.yaml
-rw-r--r--  0 0      0        1842 Nov 30 13:19 echoserver/values.yaml
-rw-r--r--  0 0      0        1755 Nov 30 13:19 echoserver/templates/NOTES.txt
-rw-r--r--  0 0      0        1802 Nov 30 13:19 echoserver/templates/_helpers.tpl
-rw-r--r--  0 0      0        1848 Nov 30 13:19 echoserver/templates/deployment.yaml
-rw-r--r--  0 0      0         922 Nov 30 13:19 echoserver/templates/hpa.yaml
-rw-r--r--  0 0      0        2083 Nov 30 13:19 echoserver/templates/ingress.yaml
-rw-r--r--  0 0      0         367 Nov 30 13:19 echoserver/templates/service.yaml
-rw-r--r--  0 0      0         324 Nov 30 13:19 echoserver/templates/serviceaccount.yaml
-rw-r--r--  0 0      0         385 Nov 30 13:19 echoserver/templates/tests/test-connection.yaml
-rw-r--r--  0 0      0         349 Nov 30 13:19 echoserver/.helmignore

Download an image

For example, for OCI images, the OCI format is more suitable:

ocm download resource ghcr.io/jensh007//github.com/acme/helloworld:1.0.0 image -O echoserver.tgz
echoserver.tgz: 46148828 byte(s) written
What happened? The file `echoserver.tgz` was downloaded.
tar xvf echoserver.tgz
x index.json
x oci-layout
x blobs
x blobs/sha256.06679f57dba70a6875e4ae5843ba2483ecab6ec48182ca8720ddc5b1863bad52
x blobs/sha256.28c6282d04f63710146ace6c7be14a40c7ee6a71a2f91316928469e4aafe0d92
x blobs/sha256.2d3e25b9e93ad26878862abee5ed02683206f6f6d57e311cdd1dedf3662b61c8
x blobs/sha256.365ec60129c5426b4cf160257c06f6ad062c709e0576c8b3d9a5dcc488f5252d
x blobs/sha256.4b12f3ef8e65aaf1fd77201670deb98728a8925236d8f1f0473afa5abe9de119
x blobs/sha256.76d46396145f805d716dcd1607832e6a1257aa17c0c2646a2a4916e47059dd54
x blobs/sha256.7fd34bf149707ca78b3bb90e4ba68fe9a013465e5d03179fb8d3a3b1cac8be27
x blobs/sha256.b0e3c31807a2330c86f07d45a6d80923d947a8a66745a2fd68eb3994be879db6
x blobs/sha256.bc391bffe5907b0eaa04e96fd638784f77d39f1feb7fbe438a1dae0af2675205
x blobs/sha256.cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229
x blobs/sha256.d5157969118932d522396fe278eb722551751c7aa7473e6d3f03e821a74ee8ec
x blobs/sha256.e0962580d8254d0b1ef35006d7e2319eb4870e63dc1f9573d2406c7c47d442d2

jq . index.json
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229",
      "size": 2400,
      "annotations": {
        "cloud.gardener.ocm/tags": "1.10",
        "org.opencontainers.image.ref.name": "1.10",
        "software.ocm/tags": "1.10"
      }
    }
  ],
  "annotations": {
    "cloud.gardener.ocm/main": "sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229",
    "software.ocm/main": "sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229"
  }
}

Download an executable

The Open Component Model allows to publish platform-specific executables. In this case, the platform specification is used by convention as extra identity for the artifacts that are contained in the component version.

Example:

ocm get componentversion ghcr.io/open-component-model/ocm//ocm.software/ocmcli:0.1.0-dev -o yaml
...
    resources:
    - name: ocmcli
      extraIdentity:
        architecture: amd64
        os: linux
      relation: local
      type: executable
      version: 0.1.0-dev
      access:
        localReference: sha256:1a8827761f0aaa897d1d4330c845121c157e905d1ff300ba5488f8c423bc7cd9
        mediaType: application/octet-stream
        type: localBlob
    - name: ocmcli
      extraIdentity:
        architecture: arm64
        os: darwin
      relation: local
      type: executable
      version: 0.1.0-dev
      access:
        localReference: sha256:9976b18dc16ae2b2b3fc56686f18f4896d44859f1ea6221f70e83517f697e289
        mediaType: application/octet-stream
        type: localBlob
...

Note, the resources shown above have the same name and type executable but a different extra-identity. If a component version complies to this convention, executables can directly be downloaded for the specified platform with the use of the -x option. If only one executable is contained in the component version, the resource name can be omitted. Example:

ocm download resource -x --latest ghcr.io/open-component-model/ocm//ocm.software/ocmcli
ocm: 52613730 byte(s) written
What happened?
ls -l
total 51M
-rwxr-xr-x  1 me staff  51M Nov 30 13:49 ocm
file ocm
ocm: Mach-O 64-bit executable arm64

With the option --latest, the latest mathching component version is used for download. With the option --constraints version constraints can be configured. For example: --constraints 0.1.x will select all patch versions of 0.1. Together with --latest the latest patch version is selected.

The option -x enables the executable download handler, which provides the x-bit of the downloaded files. Additionally it filters all matching resources for executables and the correct platform.

Download a full component version

Download entire component versions using the ocm download componentversion command:

ocm download componentversions ${OCM_REPO}//${COMPONENT}:${VERSION} -O helloworld
helloworld: downloaded

The result is a component archive. This can then be modified using the ocm add ... commands shown earlier.

What happened? The component version was downloaded.
tree helloworld2
├── blobs
└── component-descriptor.yaml

The blobs directory is empty because, during the upload to the OCI registry, the local helmchart blob was transformed to a regular OCI artifact. The access method in the component descriptor has been modified to ociArtifact.

Download OCI Artifacts

Download OCI artifacts from an OCI registry, such as OCI images, with the ocm download artifacts command:

ocm download artifact ${OCM_REPO}/${COMPONENT}:${VERSION} -O echoserver
echoserver: downloaded
What happened? The OCI image `echoserver` was downloaded.
tree echoserver
echoserver
├── blobs
│   ├── sha256.1c1af427d477202d102c141f27d3be0f5b6595e2948a82ec58987560c1915fea
│   ├── sha256.47eacca4cbed4b63c17e044d3c87a33d9bd1f88a9e76fa0ab051e48b0a3cd7ec
│   └── sha256.ea8e5b44cd1aff1f3d9377d169ad795be20fbfcd58475a62341ed8fb74d4788c
├── index.json
└── oci-layout

Transport OCM component versions

The section Bundle composed components explained how to bundle multiple component version into a transport archive.

During the transfer, it is possible to include component references as local blobs. It is also possible to include references in a recursive way.

Here is an example of a recursive transfer from one OCI registry to another, which includes resources and references:

ocm transfer componentversion --recursive --copy-resources ${OCM_REPO}//${COMPONENT}:${VERSION} eu.gcr.io/acme/
transferring version "github.com/acme/helloworld:1.0.0"...
...resource 0(github.com/acme/helloworld/echoserver:0.1.0)...
...adding component version...
1 versions transferred

The OCM CLI’s transfer command can be used to transfer component versions, component archives, transport archives and artifacts. See ocm transfer -h for more information.

More examples on the transport archive can be found in appendix A.

Sign component versions

Sign component versions to ensure integrity along a transport chain.

Signing requires a key pair, a signature, and optionally an issuer as well as an algorithm and a name for the signature.

A component version can have multiple signatures with different names. A normalization of the component version is used for signing. See appendix C for more details. Currently only signing according RSA PKCS #1 v1.5 signature algorithm supported.

Create a key pair using the OCM CLI:

ocm create rsakeypair acme.priv

This will create two files. One named acme.priv for the private key and for convenience one named acme.pub for the public key.

Use the sign componentversion command to sign a component version:

ocm sign componentversion --signature acme-sig --private-key=acme.priv ${OCM_REPO}//${COMPONENT}:${VERSION}

You can also sign a common transport archive before uploading to a component repository:

ocm sign componentversion --signature acme-sig --private-key=acme.priv ctf-hello-world
applying to version "github.com/acme/helloworld:1.0.0"...
successfully signed github.com/acme/helloworld:1.0.0 (digest sha256:46615253117b7217903302d172a45de7a92f2966f6a41efdcc948023ada318bc)
What happened?

Digests will be created for all described artifacts and referenced component versions. Then for the top-level component versions the component-version digests are signed. Signature and digests are stored in the component descriptor(s):

jq . ${CTF_ARCHIVE}/artifact-index.json
{
  "schemaVersion": 1,
  "artifacts": [
    {
      "repository": "component-descriptors/github.com/acme/helloworld",
      "tag": "1.0.0",
      "digest": "sha256:8c6b8c5a63a09d96d2a60b50adbd47f06b31be6e9d3e8618177c60fb47ec4bb2"
    }
  ]
}

Beside the digests of the component descriptor layer nothing has changed:

jq . ${CTF_ARCHIVE}/blobs/sha256.8c6b8c5a63a09d96d2a60b50adbd47f06b31be6e9d3e8618177c60fb47ec4bb2
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.ocm.software.component.config.v1+json",
    "digest": "sha256:23225a4bfd2bacd575ec5317a25a0dd63702594f5859fbc3a4c4301453ac311a",
    "size": 201
  },
  "layers": [
    {
      "mediaType": "application/vnd.ocm.software.component-descriptor.v2+yaml+tar",
      "digest": "sha256:1f8c7801b2b35768b0eb9c919683ffcd0af24d8135beaccb7146af56cb2981d9",
      "size": 3584
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+tar+gzip",
      "digest": "sha256:2a958a5e8e9cca1b4e5b3cce510db9058f0117d09ce8c0981523230aa5d0e3d0",
      "size": 4714
    }
  ]
}
tar xvf ${CTF_ARCHIVE}/blobs/sha256.1f8c7801b2b35768b0eb9c919683ffcd0af24d8135beaccb7146af56cb2981d9 -O - component-descriptor.yaml
meta:
  schemaVersion: v2
component:
  name: github.com/acme/helloworld
  version: 1.0.0
  provider: acme.org
  resources:
  - access:
      localReference: sha256:2a958a5e8e9cca1b4e5b3cce510db9058f0117d09ce8c0981523230aa5d0e3d0
      mediaType: application/vnd.oci.image.manifest.v1+tar+gzip
      referenceName: github.com/acme/helloworld/echoserver:0.1.0
      type: localBlob
    digest:
      hashAlgorithm: sha256
      normalisationAlgorithm: ociArtifactDigest/v1
      value: 7b1614e9de1daee6334c91fce087e4365ee30f8f4da783ae81c27c6a81718b1d
    name: chart
    relation: local
    type: helmChart
    version: 1.0.0
  - access:
      imageReference: gcr.io/google_containers/echoserver:1.10
      type: ociArtifact
    digest:
      hashAlgorithm: sha256
      normalisationAlgorithm: ociArtifactDigest/v1
      value: cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229
    name: image
    relation: external
    type: ociImage
    version: 1.0.0
  componentReferences: []
  repositoryContexts: []
  sources: []
signatures:
- digest:
    hashAlgorithm: sha256
    normalisationAlgorithm: jsonNormalisation/v1
    value: 023accb95490e1cf00926ddec95aadac599528bd987f6c1f0b5440c1bc51add3
  name: acme-sig
  signature:
    algorithm: RSASSA-PKCS1-V1_5
    mediaType: application/vnd.ocm.signature.rsa
    value: 6538f0f1ddb436008c4f82a84cfa92893e44cca1f2363f9da786ab632bca92f6498d912f2e4dfcdd9fa24078be83ba4f56851fa7b1235526c11cf5c9bd923676acaecb0e19f3996ac96a7334a4b4dcbf0b33479e90dd9500ea4fd5e914e17edb41c49ead6b92b313d1b79c612309b743399a2284f19a3e98c383122aa0045766394de700b8db96f4e69c6df2238c149660e5e4f8beaec45737a7ec2ddf36aa0c2042fce298c5ef2f823612229f013c147a19afe23fe81afe31200a3c2ad77485f8e9f8f01d5faba64c484b673e42a49082e1d20fb5c75616896007432e7f1b60da1591c756f4c6fab98f4125d13d7790adb41dd46717c67e92f2de6fb7c8a6c3

Signing with certificates

The public key from the last example cannot be validated. This can be changed by using a certificate instead of a pure public key. The certificate is signed by a CA. This ensures the authenticity of the described public key. Additionally the common name of the certificate is validated against the issuer attribute of the signature stored in the component descriptor.

To create a certificate use the command:

ocm create rsakeypair --cacert ca.cert --cakey ca.priv CN=acme.org acme.priv

You can use additional attributes of the certificate like O, OU or C. See usage for details. The certificate can be requested by any offical certificate authority instead. It requires the usage types x509.KeyUsageDigitalSignature and x509.ExtKeyUsageCodeSigning.

For signing the component version you need to provide the issuer then:

ocm sign componentversion --signature acme-sig --private-key=acme.priv --issuer acme.org ${OCM_REPO}//${COMPONENT}:${VERSION}

Now the issuer will be stored along the signature and will be checked when verifying with the certificate instead of the public key.

Signature Verification

You can verify a signed component version. Therefore a public or a certificate provided by the signer is required. If a certificate is provided it is validated according its certificate chain. If not an official CA is used you need the certificate of the used root CA.

To verify the signature of a component version use

ocm verify componentversions --signature acme-sig --public-key=acme.pub ctf-hello-world
applying to version "github.com/acme/helloworld:1.0.0"...
successfully verified github.com/acme/helloworld:1.0.0 (digest sha256:46615253117b7217903302d172a45de7a92f2966f6a41efdcc948023ada318bc)