Docker Donates the cnab-to-oci Library to cnab.io

Silvin Lubecki

Feb 12 2020

Docker is proud and happy to announce the donation of our cnab-to-oci library to the CNAB project 🎉. This project was created last year after Microsoft and Docker moved the CNAB specification to the Linux Foundation’s Joint Development Foundation. At that time, the CNAB specification repository was moved from the deislab GitHub organization to the new cnabio organization. The reference implementations – cnab-go which is the Golang library implementation of the specification and duffle which is the CLI reference implementation – were also moved.

What is cnab-to-oci for?

Docker helped with the development of the CNAB specification and its reference implementations, and led the work on the cnab-to-oci library for sharing a CNAB bundle using an existing container registry. This library is now used by 3 CNAB tools, Docker App, Porter and duffle, as well as Docker Hub. It successfully demonstrated how to push, pull and share a CNAB bundle using a registry. This work will be used as a foundation for the future CNAB Registries specification.

The transfer is already in effect, so starting now please refer to github.com/cnabio/cnab-to-oci in your Golang imports.

How does cnab-to-oci store a CNAB bundle into a registry?

As you may know, the OCI image specification introduces two main objects: the OCI Manifest and the OCI Image Index. The first one is well known and represents the classic Docker image. The other one was, at first, used to store multi-architecture images (see nginx as an example).

But what you may not know is that the specification doesn’t restrict the use of OCI Indexes to multi-arch images. You can store almost anything you want, as long as you meet the specification, and it is quite open.

cnab-to-oci uses this openness to push the bundle.json, but also the invocation image and the component images (or service images for a Docker App). It pushes everything in the same repository, so one has the guarantee that when someone pulls her/his bundle, all the components can be pulled as well.

Demo Time

While cnab-to-oci is implemented as a library that can be used by other tools, the repository contains a handy CLI tool that can perform push and pull of any CNAB bundle.json.

With the following command we push a bundle example to the Docker Hub repository. It pushes all the manifests found in the bundle, then creates an OCI Index and pushes it at the end. The digest we get as a result is pointing to the OCI Index of the bundle.

$ make bin/cnab-to-oci

$ ./bin/cnab-to-oci push examples/helloworld-cnab/bundle.json -t hubusername/repo:demo –log-level=debug –auto-update-bundle

DEBU[0000] Fixing up bundle docker.io/hubusername/repo:demo
DEBU[0000] Updating entry in relocation map for “cnab/helloworld:0.1.1”
Starting to copy image cnab/helloworld:0.1.1…
Completed image cnab/helloworld:0.1.1 copy
DEBU[0004] Bundle fixed
DEBU[0004] Pushing CNAB Bundle docker.io/hubusername/repo:demo
DEBU[0004] Pushing CNAB Bundle Config
DEBU[0004] Trying to push CNAB Bundle Config
DEBU[0004] CNAB Bundle Config Descriptor
DEBU[0004] {
  “mediaType”: “application/vnd.cnab.config.v1+json”,
  “digest”: “sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b”,
  “size”: 498
}
DEBU[0005] Trying to push CNAB Bundle Config Manifest
DEBU[0005] CNAB Bundle Config Manifest Descriptor
DEBU[0005] {
  “mediaType”: “application/vnd.oci.image.manifest.v1+json”,
  “digest”: “sha256:6ec4fd695cace0e3d4305838fdf9fcd646798d3fea42b3abb28c117f903a6a5f”,
  “size”: 188
}
DEBU[0006] Failed to push CNAB Bundle Config Manifest, trying with a fallback method
DEBU[0006] Trying to push CNAB Bundle Config
DEBU[0006] CNAB Bundle Config Descriptor
DEBU[0006] {
  “mediaType”: “application/vnd.oci.image.config.v1+json”,
  “digest”: “sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b”,
  “size”: 498
}
DEBU[0006] Trying to push CNAB Bundle Config Manifest
DEBU[0006] CNAB Bundle Config Manifest Descriptor
DEBU[0006] {
  “mediaType”: “application/vnd.oci.image.manifest.v1+json”,
  “digest”: “sha256:b9616da7500f8c7c9a5e8d915714cd02d11bcc71ff5b4fd190bb77b1355c8549”,
  “size”: 193
}
DEBU[0006] CNAB Bundle Config pushed
DEBU[0006] Pushing CNAB Index
DEBU[0006] Trying to push OCI Index
DEBU[0006] {“schemaVersion”:2,”manifests”:[{“mediaType”:”application/vnd.oci.image.manifest.v1+json”,”digest”:”sha256:b9616da7500f8c7c9a5e8d915714cd02d11bcc71ff5b4fd190bb77b1355c8549″,”size”:193,”annotations”:{“io.cnab.manifest.type”:”config”}},{“mediaType”:”application/vnd.docker.distribution.manifest.v2+json”,”digest”:”sha256:a59a4e74d9cc89e4e75dfb2cc7ea5c108e4236ba6231b53081a9e2506d1197b6″,”size”:942,”annotations”:{“io.cnab.manifest.type”:”invocation”}}],”annotations”:{“io.cnab.keywords”:”[\”helloworld\”,\”cnab\”,\”tutorial\”]”,”io.cnab.runtime_version”:”v1.0.0″,”org.opencontainers.artifactType”:”application/vnd.cnab.manifest.v1″,”org.opencontainers.image.authors”:”[{\”name\”:\”Jane Doe\”,\”email\”:\”jane.doe@example.com\”,\”url\”:\”https://example.com\”}]”,”org.opencontainers.image.description”:”A short description of your bundle”,”org.opencontainers.image.title”:”helloworld”,”org.opencontainers.image.version”:”0.1.1″}}
DEBU[0006] OCI Index Descriptor
DEBU[0006] {
  “mediaType”: “application/vnd.oci.image.index.v1+json”,
  “digest”: “sha256:fcee8577f3acc8ddc6e0280e6d1eb15be70bdff460fe7353abf917a872487af2”,
  “size”: 926
}
DEBU[0007] CNAB Index pushed
DEBU[0007] CNAB Bundle pushed
Pushed successfully, with digest “sha256:fcee8577f3acc8ddc6e0280e6d1eb15be70bdff460fe7353abf917a872487af2”

Let’s check that our bundle has been pushed on Docker Hub:

We can now pull our bundle back from the registry. It will only fetch the bundle.json file, but as you may notice this now has a digested reference for the image manifest of every component, inside the same registry repository. The Docker Engine will pull any images required by the bundle at runtime. So pulling a bundle is a lightweight process.

$ ./bin/cnab-to-oci pull hubusername/repo:demo –log-level=debug

DEBU[0000] Pulling CNAB Bundle docker.io/hubusername/repo:demo
DEBU[0000] Getting OCI Index Descriptor
DEBU[0001] {
  “mediaType”: “application/vnd.oci.image.index.v1+json”,
  “digest”: “sha256:fcee8577f3acc8ddc6e0280e6d1eb15be70bdff460fe7353abf917a872487af2”,
  “size”: 926
}
DEBU[0001] Fetching OCI Index sha256:fcee8577f3acc8ddc6e0280e6d1eb15be70bdff460fe7353abf917a872487af2
DEBU[0001] {
  “schemaVersion”: 2,
  “manifests”: [
    {
      “mediaType”: “application/vnd.oci.image.manifest.v1+json”,
      “digest”: “sha256:b9616da7500f8c7c9a5e8d915714cd02d11bcc71ff5b4fd190bb77b1355c8549”,
      “size”: 193,
      “annotations”: {
        “io.cnab.manifest.type”: “config”
      }
    },
    {
      “mediaType”: “application/vnd.docker.distribution.manifest.v2+json”,
      “digest”: “sha256:a59a4e74d9cc89e4e75dfb2cc7ea5c108e4236ba6231b53081a9e2506d1197b6”,
      “size”: 942,
      “annotations”: {
        “io.cnab.manifest.type”: “invocation”
      }
    }
  ],
  “annotations”: {
    “io.cnab.keywords”: “[\”helloworld\”,\”cnab\”,\”tutorial\”]”,
    “io.cnab.runtime_version”: “v1.0.0”,
    “org.opencontainers.artifactType”: “application/vnd.cnab.manifest.v1”,
    “org.opencontainers.image.authors”: “[{\”name\”:\”Jane Doe\”,\”email\”:\”jane.doe@example.com\”,\”url\”:\”https://example.com\”}]”,
    “org.opencontainers.image.description”: “A short description of your bundle”,
    “org.opencontainers.image.title”: “helloworld”,
    “org.opencontainers.image.version”: “0.1.1”
  }
}
DEBU[0001] Getting Bundle Config Manifest Descriptor
DEBU[0001] {
  “mediaType”: “application/vnd.oci.image.manifest.v1+json”,
  “digest”: “sha256:b9616da7500f8c7c9a5e8d915714cd02d11bcc71ff5b4fd190bb77b1355c8549”,
  “size”: 193,
  “annotations”: {
    “io.cnab.manifest.type”: “config”
  }
}
DEBU[0001] Getting Bundle Config Manifest sha256:b9616da7500f8c7c9a5e8d915714cd02d11bcc71ff5b4fd190bb77b1355c8549
DEBU[0001] {
  “schemaVersion”: 2,
  “config”: {
    “mediaType”: “application/vnd.oci.image.config.v1+json”,
    “digest”: “sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b”,
    “size”: 498
  },
  “layers”: null
}
DEBU[0001] Fetching Bundle sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b
DEBU[0002] {
  “schemaVersion”: “v1.0.0”,
  “name”: “helloworld”,
  “version”: “0.1.1”,
  “description”: “A short description of your bundle”,
  “keywords”: [
    “helloworld”,
    “cnab”,
    “tutorial”
  ],
  “maintainers”: [
    {
      “name”: “Jane Doe”,
      “email”: “jane.doe@example.com”,
      “url”: “https://example.com”
    }
  ],
  “invocationImages”: [
    {
      “imageType”: “docker”,
      “image”: “cnab/helloworld:0.1.1”,
      “contentDigest”: “sha256:a59a4e74d9cc89e4e75dfb2cc7ea5c108e4236ba6231b53081a9e2506d1197b6”,
      “size”: 942,
      “mediaType”: “application/vnd.docker.distribution.manifest.v2+json”
    }
  ]
}

cnab-to-oci has been integrated with Docker App in the last beta release v0.9.0-beta1, to let you push and pull your entire application with the same UX as pushing a regular Docker container image. As Docker App is a standard CNAB runtime, it can also run this generic CNAB example:

$ docker app pull hubusername/repo:demo
Successfully pulled “helloworld” (0.1.1) from docker.io/hubusername/repo:demo

$ docker app run hubusername/repo:demo
Port parameter was set to 
Install action
Action install complete for upbeat_nobel
App “upbeat_nobel” running on context “default”

Want to Know More?

If you’re interested in getting more details about CNAB, a few blog posts are available:

Please note that we will give a talk about this topic at KubeCon Europe 2020: “Sharing is Caring! Push your Cloud Application to an OCI Registry – Silvin Lubecki & Djordje Lukic”

And of course, you can also find more information directly on the cnab-to-oci GitHub repository.

Contributions are welcome!!! 🤗