Working with image digests

This guide explains how to leverage the automatic image digest resolution feature when creating a Package, which allows the creator to easily replace the image tags with their respective digests inside the packaged resources.

This enables a stricter control over which images will be included in the package, since the digest is the ultimate way to uniquely identify an image, while the tag can identify different ones over time (think of latest that gets updated every time).

All files used during this guide are available in the package-operator/examples repository.

1. Start

_Please refer to the files in /3_image_digests/1_start for this step. It is based on step 1 of the application packaging guide with the addition of some templating. Make sure you understand these concepts before continuing.

Add images to PackageManifest

In the .spec.images field of the manifest.yaml file, specify the list of images needed, each one associated with its own custom label:

apiVersion: manifests.package-operator.run/v1alpha1
kind: PackageManifest
metadata:
  name: nginx
spec:
  ...
  images:
  - name: webserver
    image: nginx:1.23.3
  - name: base
    image: registry.access.redhat.com/ubi9/ubi-minimal:9.1

Resolve digests

The Package Operator CLI supports a new subcommand: update, which resolves the image tags in the manifests to their corresponding digests. This information is then stored in a new file called manifest.lock.yaml (a.k.a. lock file).

The lock file is mandatory if the manifest spec contains some images, otherwise the build will fail.

To generate or update the lock file, issue the following command:

kubectl package update .  # this assumes we're in the package root folder

Here is an example of the output (digests are the ones at the time of writing):

apiVersion: manifests.package-operator.run/v1alpha1
kind: PackageManifestLock
metadata:
  creationTimestamp: "2023-03-09T13:45:10Z"
spec:
  images:
  - digest: sha256:aa0afebbb3cfa473099a62c4b32e9b3fb73ed23f2a75a65ce1d4b4f55a5c2ef2
    image: nginx:1.23.3
    name: webserver
  - digest: sha256:61925d31338b7b41bfd5b6b8cf45eaf80753d415b0269fc03613c5c5049b879e
    image: registry.access.redhat.com/ubi9/ubi-minimal:9.1
    name: base

IMPORTANT: if a new image is pushed to its registry, causing an already specified tag to point to a different digest, the value in the lock file won’t be updated until the update subcommand is explicitly reissued.

Use images map in the templates

To use these values in the real package resources, the templating context is enriched with a map named images, in which each image is identified by a key, consisting of the custom label, pointing to the full image reference that uses the resolved digest.

Since this is a standard map, it can be used anywhere in any type of resource with the standard templating syntax.

Here we have two examples. The first one is a Deployment that uses the dot syntax to access the map:

apiVersion: apps/v1
kind: Deployment
metadata:
  ...
  name: nginx-deployment
  ...
spec:
  ...
  template:
    ...
    spec:
      containers:
        - name: nginx
          image: "{{.images.webserver}}"
          ...

and the second is a ConfigMap that uses the index syntax instead:

apiVersion: v1
kind: ConfigMap
metadata:
  ...
  name: example-configmap
  ...
data:
  image_with_digest: "{{index .images "base"}}"

Resolved values in deployed package

Here is an examples of the same two resources once deployed in a cluster:

The Deployment first:

apiVersion: apps/v1
kind: Deployment
metadata:
  ...
  name: nginx-deployment
  ...
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: nginx
        image: docker.io/library/nginx@sha256:aa0afebbb3cfa473099a62c4b32e9b3fb73ed23f2a75a65ce1d4b4f55a5c2ef2
        ...

and finally the ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  ...
  name: example-configmap
  ...
data:
  image_with_digest: registry.access.redhat.com/ubi9/ubi-minimal@sha256:61925d31338b7b41bfd5b6b8cf45eaf80753d415b0269fc03613c5c5049b879e