# Helm: new `imageRegistry`, `imageNamespace`, and `imageSuffix` values > [!NOTE] > > This document goes over the two main use-cases regarding the design proposal in [PR 8115](https://github.com/cert-manager/cert-manager/pull/8115). ## Problem The cert-manager's Helm chart is notoriously painful to use with OCI mirroring registries such as Artifactory, as shown by the multiple GitHub issues and PRs: - https://github.com/cert-manager/cert-manager/issues/6160, - https://github.com/cert-manager/cert-manager/issues/7101, - https://github.com/cert-manager/cert-manager/pull/7558, - https://github.com/cert-manager/cert-manager/pull/8115. In many other Helm charts, all you have to do is to set the `global.image.registry` to your Artifactory's mount point. But with cert-manager, you will have to rewrite the entire image names for all 4 images. ## Proposed solution We would like to add three additional Helm values to help with two use-cases. Here is what the default values will look like: ```yaml= imageRegistry: quay.io imageNamespace: jetstack imageSuffix: "" ``` Note that `imageRegistry` doesn't have to be an actual OCI registry (i.e., host:port). Like many other Helm charts do, we use a looser definition: "the thing that comes before the Docker repository". The Docker repository would be jetstack/cert-manager-webhook, in this case. You can read more about the term "repository" in the section [Have we been using the term repository wrong?](#Have-we-been-using-the-term-repository-wrong). Here is how images are constructed with these default values: ``` quay.io/jetstack/cert-manager-startupapicheck <-----> <------> <--------------------------><---> imageRegistry imageNamespace image.name imageSuffix (empty) ``` ## Use-case 1: Artifactory mirroring OSS images Users of the helm chart with Artifactory-mirrored images have to repeat the same thing over and over: ```yaml= image: registry: artifactory.domain.com/docker repository: jetstack/cert-manager-controller acmesolver: image: registry: artifactory.domain.com/docker repository: jetstack/cert-manager-acmesolver webhook: image: registry: artifactory.domain.com/docker repository: jetstack/cert-manager-webhook cainjector: image: registry: artifactory.domain.com/docker repository: jetstack/cert-manager-cainjector startupapicheck: image: registry: artifactory.domain.com/docker repository: jetstack/cert-manager-startupapicheck ``` This is because we chose, [in 2017](https://github.com/cert-manager/cert-manager/pull/145), to use the full image name as the default for `repository`, instead of just the part after the registry: ```yaml= # Current defaults: image: # registry: quay.io repository: quay.io/jetstack/cert-manager-controller ``` Once we add the three new values, Artifactory users will be able to do: ```yaml= imageRegistry: artifactory.domain.com/docker ``` Here is how the image is reconstructed: ``` artifactory.domain.com/docker/jetstack/cert-manager-startupapicheck <---------------------------> <------> <--------------------------><---> imageRegistry imageNamespace image.name imageSuffix ``` ## Use-case 2: CyberArk-specific use-cases Right now, CyberArk customers have to do: ```yaml image: repository: private-registry.venafi.cloud/cert-manager/cert-manager-controller acmesolver: image: repository: private-registry.venafi.cloud/cert-manager/cert-manager-acmesolver webhook: image: repository: private-registry.venafi.cloud/cert-manager/cert-manager-webhook cainjector: image: repository: private-registry.venafi.cloud/cert-manager/cert-manager-cainjector startupapicheck: image: repository: private-registry.venafi.cloud/cert-manager/cert-manager-startupapicheck ``` ### Use-case 2.1: non-FIPS + CyberArk registry When using non-FIPS images, they will be able to use: ```yaml imageRegistry: private-registry.venafi.cloud imageNamespace: cert-manager ``` Here is how the image is reconstructed: ``` private-registry.venafi.cloud/cert-manager/cert-manager-startupapicheck <---------------------------> <----------> <--------------------------><---> imageRegistry imageNamespace image.name imageSuffix ``` ### Use-case 2.2: FIPS + CyberArk registry If they are using our STS FIPS images, they will be able to use: ```yaml imageRegistry: private-registry.venafi.cloud imageNamespace: cert-manager imageSuffix: -fips ``` Here is how the image is reconstructed: ``` private-registry.venafi.cloud/cert-manager/cert-manager-startupapicheck-fips <---------------------------> <----------> <--------------------------><---> imageRegistry imageNamespace image.name imageSuffix ``` ### Use-case 2.3: non-FIPS + Artifactory mirroring CyberArk images And if they are using Artifactory as a mirror, imagining that their images are: ``` artifactory.domain.com/venafi/cert-manager/cert-manager-controller ``` Then they can do: ```yaml imageRegistry: artifactory.domain.com/venafi imageNamespace: cert-manager ``` ### Use-case 2.4: FIPS + Artifactory mirroring CyberArk images ```yaml imageRegistry: artifactory.domain.com/venafi imageNamespace: cert-manager imageSuffix: -fips ``` ## Coherence with the other cert-manager Helm charts Here is a comparison of how other cert-manager-related projects structure their Helm values for images: | Project | Where are images configured? | Existing default for `image.registry` | Existing `image.repository` | Notable differences | |-------------------------|-----------------------------------------------|---------------------------------------|-------------------------------------------------------------|-----------------------------------------------------------------------------------| | csi-driver | `image.*` + per-component images | no default set | `quay.io/jetstack/cert-manager-csi-driver` | Per-component sidecars (node registrar, liveness) with their own image blocks | | istio-csr | `image.*` | no default set | `quay.io/jetstack/cert-manager-istio-csr` | Standard single `image` block | | google-cas-issuer | `image.*` | "" | `quay.io/jetstack/cert-manager-google-cas-issuer` | Registry field exists but default is empty | | aws-privateca-issuer | `image.*` | field doesn't exist | `public.ecr.aws/k1n1h4h4/cert-manager-aws-privateca-issuer` | No registry override field at all | | approver-policy | `image.*` | no default set | `quay.io/jetstack/cert-manager-approver-policy` | Standard single `image` block | | trust-manager | `image.*` + `defaultPackageImage.*` | no default set | `quay.io/jetstack/trust-manager` | Has `global.rbac` only; no global image settings | | csi-driver-spiffe | `image.*` with repo map (`driver`,`approver`) | no default set | See *(1)* | `image.repository` is a map, not a string | | openshift-routes | `image.*` | no default set | `ghcr.io/cert-manager/cert-manager-openshift-routes` | Standard single `image` block | | cert-manager (proposal) | `image.*` + per-component images | no default set | `quay.io/jetstack/cert-manager-csi-driver-spiffe` | each componant has its own `<component>.image.*` block, controller uses `image.*` | *(1)* For csi-driver-spiffe, the `image.repository` is the only one that uses a map with keys `driver` and `approver`, each containing full image names, i.e., ```yaml= image: repository: driver: quay.io/jetstack/cert-manager-csi-driver-spiffe approver: quay.io/jetstack/cert-manager-csi-driver-spiffe-approver ``` <!-- curl -sSL https://raw.githubusercontent.com/cert-manager/csi-driver/refs/heads/main/deploy/charts/csi-driver/values.yaml | yq .image.repository curl -sSL https://raw.githubusercontent.com/cert-manager/istio-csr/refs/heads/main/deploy/charts/istio-csr/values.yaml | yq .image.repository curl -sSL https://raw.githubusercontent.com/cert-manager/google-cas-issuer/refs/heads/main/deploy/charts/google-cas-issuer/values.yaml | yq .image.repository curl -sSL https://raw.githubusercontent.com/cert-manager/aws-privateca-issuer/refs/heads/main/charts/aws-pca-issuer/values.yaml | yq .image.repository curl -sSL https://raw.githubusercontent.com/cert-manager/approver-policy/refs/heads/main/deploy/charts/approver-policy/values.yaml | yq .image.repository curl -sSL https://raw.githubusercontent.com/cert-manager/trust-manager/refs/heads/main/deploy/charts/trust-manager/values.yaml | yq .image.repository curl -sSL https://raw.githubusercontent.com/cert-manager/csi-driver-spiffe/refs/heads/main/deploy/charts/csi-driver-spiffe/values.yaml | yq .image.repository curl -sSL https://raw.githubusercontent.com/cert-manager/openshift-routes/refs/heads/main/deploy/charts/openshift-routes/values.yaml | yq .image.repository --> ## Have we been using the term repository wrong? Short answer: yes, we use it the wrong way. "Docker repository" dates back to the beginning of Docker. Originally, this term belonged to the "Docker hosted" images. That's why a "Docker repository" doesn't have a domain: docker.io was assumed. Examples: - `ubuntu` (which is a shorthand for`library/ubuntu`) - `nicolaka/netshoot` Over time, other registries were created, and things became fuzzy. The term "image" also shifted from being just a repository and a tag (`ubuntu:latest`) to being a registry host, repository, and tag (`docker.io/library/ubuntu:latest`). The [OCI distribution spec](https://specs.opencontainers.org/distribution-spec/) clarified the meaning of "repository": > Repository: a scope for API calls on a registry for a collection of content (including manifests, blobs, and tags). > > To pull a manifest, perform a GET request to a URL in the following form: > > ``` > /v2/<name>/manifests/<reference> > ``` > > `<name>` refers to the **namespace of the repository**. <reference> MUST be either (a) the digest of the manifest or (b) a tag. The <reference> MUST NOT be in any other format. An "OCI repository" isn't `ubuntu`, nor `docker.io/library/ubuntu`... it's just scope for API calls. The identifiers `ubuntu` and such are "OCI repository namespaces" (shortened as "repository names" in the rest of the spec). Thus, an OCI repository doesn't contain the registry host. Many projects who started with docker.io images migrated away. In their Helm charts, they used the `repository` value to mean the "Docker-hosted repository". For example, that's what the ArgoCD Helm chart looked like a long time ago: ```yaml image: repository: argoproj/argocd ``` When ArgoCD moved away from docker.io [in 2021](https://github.com/argoproj/argo-helm/commit/3ba4cdb1ca9bf1c7bdc561b2255886ff4cb17949#diff-9eb59d56febda5e405748483497dfdc0c2dfbe9b4f386a12f0180c98d99051e0L366), the project kept using the term "repository": ```diff global: image: - repository: argoproj/argocd + repository: quay.io/argoproj/argocd ``` There is still no `global.image.registry` in ArgoCD's [values.yaml](https://github.com/argoproj/argo-helm/blob/6780539878c268460e3af9b3aefd54b15993a5d1/charts/argo-cd/values.yaml#L60) as of today. The cert-manager also started with docker.io. In 2017, its [values.yaml](https://github.com/cert-manager/cert-manager/commit/7194492447d5ea7bf5e0075cf87f02c3a3c2fb18) looked like this: ```yaml image: repository: jetstackexperimental/cert-manager-controller ``` KEDA did things somewhat differently... In 2020, they used to use the term "image" (which corresponds to the first in the Pod spec). When they [changed](https://github.com/kedacore/charts/commit/bfabf0fe055ae0af76ead237da57555d8e2d60a8#diff-3b4e4c10436059483f20fa5b4b2e507a0bcead63f71af2c02ca41dbff3c8b3adR7) it to "repository", they kept the same semantics: ```diff image: - keda: docker.io/kedacore/keda:2.0.0-rc - metricsApiServer: docker.io/kedacore/keda-metrics-apiserver:2.0.0-rc + keda: + repository: docker.io/kedacore/keda + metricsApiServer: + repository: docker.io/kedacore/keda-metrics-apiserver ``` When KEDA migrated away from docker.io [in 2021](https://github.com/kedacore/charts/commit/d5e7ec2e29cbb17a992dd5b9c352773c30a3abfd), it became: ```diff image: keda: - repository: docker.io/kedacore/keda + repository: ghcr.io/kedacore/keda metricsApiServer: - repository: docker.io/kedacore/keda-metrics-apiserver + repository: ghcr.io/kedacore/keda-metrics-apiserver ``` They changed to using a `registry` and `repository` later on: ```yaml image: keda: registry: ghcr.io repository: kedacore/keda metricsApiServer: registry: ghcr.io repository: kedacore/keda-metrics-apiserver ``` ## Erik's point: the problem with `imagePrefix` ```yaml= imagePrefix: artifactory.domain.com/docker/jetstack image: name: cert-manager-controller acmesolver: image: name: cert-manager-acmesolver webhook: image: name: cert-manager-webhook cainjector: image: name: cert-manager-cainjector startupapicheck: image: name: cert-manager-startupapicheck ``` ## Notes For consistency with other charts, let's go with the name "registry" even though it isn't the right term. We should not use `global.image.registry` because... that's why we went with `imageRegistry`. The `imageSuffix` isn't really as important, let's not have it as part of this design. ## Next things to be done next - [ ] Felix: Create a new design (PR) since Tim's design is somewhat different. Adjust the hackmd document to the format - [ ] Felix: Present to Atanas the baking + `imageRegistry` solution. - [x] Mael: investigate artifactory (not required but I want to) - [ ] Felix: Prototype it to make sure we can implement it in a non-breaking way