diff --git a/image-mapper/README.md b/image-mapper/README.md index 9ee093d..4a11c05 100644 --- a/image-mapper/README.md +++ b/image-mapper/README.md @@ -78,3 +78,15 @@ docker run -it --rm image-mapper ghcr.io/stakater/reloader:v1.4.1 # Or, pass a list of images from a text file docker run -i --rm image-mapper -- - < images.txt ``` + +## Development + +You can run integration tests against the actual catalog endpoint by setting +`IMAGE_MAPPER_RUN_INTEGRATION_TESTS=1`: + +``` +IMAGE_MAPPER_RUN_INTEGRATION_TESTS=1 go test ./... +``` + +This identifies regressions in the mapping logic or the catalog data by +recording known matches. diff --git a/image-mapper/internal/mapper/mapper.go b/image-mapper/internal/mapper/mapper.go index c8e4fda..67c497a 100644 --- a/image-mapper/internal/mapper/mapper.go +++ b/image-mapper/internal/mapper/mapper.go @@ -1,11 +1,8 @@ package mapper import ( - "bytes" "context" - "encoding/json" "fmt" - "net/http" "slices" "strings" @@ -44,46 +41,6 @@ func NewMapper(ctx context.Context, opts ...Option) (*Mapper, error) { return m, nil } -// Repo describes a repo in the catalog -type Repo struct { - Name string `json:"name"` - CatalogTier string `json:"catalogTier"` - Aliases []string `json:"aliases"` -} - -func listRepos(ctx context.Context) ([]Repo, error) { - c := &http.Client{} - - buf := bytes.NewReader([]byte(`{"query":"query OrganizationImageCatalog($organization: ID!) {\n repos(filter: {uidp: {childrenOf: $organization}}) {\n name\n aliases\n catalogTier\n }\n}","variables":{"excludeDates":true,"excludeEpochs":true,"organization":"ce2d1984a010471142503340d670612d63ffb9f6"}}`)) - req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://data.chainguard.dev/query?id=PrivateImageCatalog", buf) - if err != nil { - return nil, fmt.Errorf("constructing request: %w", err) - } - req.Header.Add("Content-Type", "application/json") - req.Header.Add("User-Agent", "image-mapper") - - resp, err := c.Do(req) - if err != nil { - return nil, fmt.Errorf("making request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } - - var data struct { - Data struct { - Repos []Repo `json:"repos"` - } `json:"data"` - } - if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { - return nil, fmt.Errorf("unmarshaling body: %w", err) - } - - return data.Data.Repos, nil -} - // MapAll returns mappings for all the images returned by the iterator func (m *Mapper) MapAll(it Iterator) ([]*Mapping, error) { mapped := make(map[string]struct{}) @@ -145,6 +102,7 @@ func (m *Mapper) Map(image string) (*Mapping, error) { for match := range matches { results = append(results, match) } + slices.Sort(results) return &Mapping{ Image: image, diff --git a/image-mapper/internal/mapper/mapper_test.go b/image-mapper/internal/mapper/mapper_test.go index 6f799ed..592bd1c 100644 --- a/image-mapper/internal/mapper/mapper_test.go +++ b/image-mapper/internal/mapper/mapper_test.go @@ -2,6 +2,7 @@ package mapper import ( "errors" + "os" "strings" "testing" @@ -263,3 +264,310 @@ type errorIterator struct { func (it *errorIterator) Next() (string, error) { return "", it.err } + +func TestMapperIntegration(t *testing.T) { + if v := os.Getenv("IMAGE_MAPPER_RUN_INTEGRATION_TESTS"); v == "" { + t.Skip() + } + + testCases := map[string][]string{ + "atmoz/sftp:alpine": { + "atmoz-sftp", + "atmoz-sftp-fips", + }, + "busybox:1.35.0": { + "busybox", + "busybox-fips", + }, + "coredns/coredns:1.11.3": { + "coredns", + "coredns-fips", + }, + "curlimages/curl:7.85.0": { + "curl", + "curl-fips", + }, + "ghcr.io/cloudnative-pg/cloudnative-pg:v1.24.4": { + "cloudnative-pg", + "cloudnative-pg-fips", + }, + "ghcr.io/cloudnative-pg/pgbouncer:1.23.0": { + "pgbouncer", + "pgbouncer-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-cloudformation:v1.20.1": { + "crossplane-aws-cloudformation", + "crossplane-aws-cloudformation-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-cloudfront:v1.20.1": { + "crossplane-aws-cloudfront", + "crossplane-aws-cloudfront-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-dynamodb:v1.20.1": { + "crossplane-aws-dynamodb", + "crossplane-aws-dynamodb-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-ec2:v1.20.1": { + "crossplane-aws-ec2", + "crossplane-aws-ec2-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-eks:v1.20.1": { + "crossplane-aws-eks", + "crossplane-aws-eks-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-firehose:v1.20.1": { + "crossplane-aws-firehose", + "crossplane-aws-firehose-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-iam:v1.20.1": { + "crossplane-aws-iam", + "crossplane-aws-iam-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-kinesis:v1.20.1": { + "crossplane-aws-kinesis", + "crossplane-aws-kinesis-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-kms:v1.20.1": { + "crossplane-aws-kms", + "crossplane-aws-kms-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-lambda:v1.20.1": { + "crossplane-aws-lambda", + "crossplane-aws-lambda-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-rds:v1.20.1": { + "crossplane-aws-rds", + "crossplane-aws-rds-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-route53:v1.20.1": { + "crossplane-aws-route53", + "crossplane-aws-route53-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-s3:v1.20.1": { + "crossplane-aws-s3", + "crossplane-aws-s3-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-sns:v1.20.1": { + "crossplane-aws-sns", + "crossplane-aws-sns-fips", + }, + "ghcr.io/crossplane-contrib/provider-aws-sqs:v1.20.1": { + "crossplane-aws-sqs", + "crossplane-aws-sqs-fips", + }, + "ghcr.io/crossplane-contrib/provider-family-aws:v1.21.1": { + "crossplane-aws", + "crossplane-aws-fips", + }, + "ghcr.io/fluxcd/flux-cli:v2.7.5": { + "flux", + "flux-fips", + }, + "ghcr.io/fluxcd/helm-controller:v1.4.5": { + "flux-helm-controller", + "flux-helm-controller-fips", + }, + "ghcr.io/fluxcd/image-automation-controller:v1.0.4": { + "flux-image-automation-controller", + "flux-image-automation-controller-fips", + }, + "ghcr.io/fluxcd/image-reflector-controller:v1.0.4": { + "flux-image-reflector-controller", + "flux-image-reflector-controller-fips", + }, + "ghcr.io/fluxcd/kustomize-controller:v1.7.3": { + "flux-kustomize-controller", + "flux-kustomize-controller-fips", + }, + "ghcr.io/fluxcd/notification-controller:v1.7.5": { + "flux-notification-controller", + "flux-notification-controller-fips", + }, + "ghcr.io/fluxcd/source-controller:v1.7.4": { + "flux-source-controller", + "flux-source-controller-fips", + }, + "hashicorp/vault-csi-provider:1.4.0": { + "vault-csi-provider", + "vault-csi-provider-fips", + }, + "hashicorp/vault:1.14.0": { + "vault", + "vault-fips", + }, + "hashicorp/vault-k8s:1.14.0": { + "vault-k8s", + "vault-k8s-fips", + }, + "influxdb:2.7.4-alpine": { + "influxdb", + }, + "oliver006/redis_exporter:v1.45.0-alpine": { + "prometheus-redis-exporter", + "prometheus-redis-exporter-fips", + }, + "opensearchproject/opensearch-dashboards:2.19.1": { + "opensearch-dashboards", + "opensearch-dashboards-fips", + }, + "opensearchproject/opensearch-operator:2.7.0": { + "opensearch-k8s-operator", + }, + "opensearchproject/opensearch:2.19.1": { + "opensearch", + }, + "percona/haproxy:2.8.5": { + "haproxy", + "haproxy-fips", + }, + "prom/mysqld-exporter:v0.16.0": { + "prometheus-mysqld-exporter", + }, + "prom/statsd-exporter:v0.26.1": { + "prometheus-statsd-exporter", + "prometheus-statsd-exporter-fips", + }, + "quay.io/argoproj/argocd:v3.2.1": { + "argocd", + "argocd-fips", + "argocd-repo-server", + "argocd-repo-server-fips", + }, + "quay.io/argoproj/argocli:latest": { + "argo-cli", + "argo-cli-fips", + }, + "quay.io/argoproj/argoexec:latest": { + "argo-exec", + "argo-exec-fips", + }, + "quay.io/argoproj/argo-events:latest": { + "argo-events", + "argo-events-fips", + }, + "quay.io/argoproj/workflow-controller:latest": { + "argo-workflowcontroller", + "argo-workflowcontroller-fips", + }, + "quay.io/jetstack/cert-manager-acmesolver:v1.15.2": { + "cert-manager-acmesolver", + "cert-manager-acmesolver-fips", + "cert-manager-acmesolver-iamguarded", + "cert-manager-acmesolver-iamguarded-fips", + }, + "quay.io/jetstack/cert-manager-cainjector:v1.15.2": { + "cert-manager-cainjector", + "cert-manager-cainjector-fips", + "cert-manager-cainjector-iamguarded", + "cert-manager-cainjector-iamguarded-fips", + }, + "quay.io/jetstack/cert-manager-controller:v1.15.2": { + "cert-manager-controller", + "cert-manager-controller-fips", + "cert-manager-controller-iamguarded", + "cert-manager-controller-iamguarded-fips", + }, + "quay.io/jetstack/cert-manager-startupapicheck:v1.15.2": { + "cert-manager-startupapicheck", + "cert-manager-startupapicheck-fips", + }, + "quay.io/jetstack/cert-manager-webhook:v1.15.2": { + "cert-manager-webhook", + "cert-manager-webhook-fips", + "cert-manager-webhook-iamguarded", + "cert-manager-webhook-iamguarded-fips", + }, + "quay.io/jetstack/cmctl:v2.4.0": { + "cert-manager-cmctl", + "cert-manager-cmctl-fips", + }, + "quay.io/jetstack/trust-manager:v0.12.0": { + "trust-manager", + "trust-manager-fips", + }, + "quay.io/minio/mc:RELEASE.2025-08-13T08-35-41Z-cpuv1": { + "minio-client", + "minio-client-fips", + }, + "quay.io/minio/minio:RELEASE.2024-10-02T17-50-41Z": { + "minio", + "minio-fips", + }, + "quay.io/minio/operator:v6.0.4": { + "minio-operator", + "minio-operator-fips", + }, + "quay.io/minio/operator-sidecar:v6.0.4": { + "minio-operator-sidecar", + "minio-operator-sidecar-fips", + }, + "quay.io/mongodb/mongodb-kubernetes-operator-version-upgrade-post-start-hook:1.0.9": { + "mongodb-kubernetes-operator-version-upgrade-post-start-hook", + "mongodb-kubernetes-operator-version-upgrade-post-start-hook-fips", + }, + "quay.io/mongodb/mongodb-kubernetes-operator:0.12.0": { + "mongodb-kubernetes-operator", + "mongodb-kubernetes-operator-fips", + }, + "quay.io/prometheus/pushgateway:v1.9.0": { + "prometheus-pushgateway", + "prometheus-pushgateway-fips", + }, + "registry.k8s.io/sig-storage/csi-attacher:v4.6.1": { + "kubernetes-csi-external-attacher", + }, + "registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.11.1": { + "kubernetes-csi-node-driver-registrar", + }, + "registry.k8s.io/sig-storage/livenessprobe:v2.13.1": { + "kubernetes-csi-livenessprobe", + }, + "registry.k8s.io/sig-storage/nfsplugin:v4.8.0": { + "kubernetes-csi-driver-nfs", + }, + "registry.k8s.io/sig-storage/snapshot-controller:v8.0.1": { + "kubernetes-csi-external-snapshot-controller", + "kubernetes-csi-external-snapshotter", + }, + "thingsboard/tb-js-executor:3.9.1": { + "thingsboard-tb-js-executor", + }, + "thingsboard/tb-mqtt-transport:3.5.1": { + "thingsboard-tb-mqtt-transport", + }, + "thingsboard/tb-node:3.5.1": { + "thingsboard-tb-node", + }, + "thingsboard/tb-web-ui:3.5.1": { + "thingsboard-tb-web-ui", + }, + "valkey/valkey:7.2.5-alpine": { + "valkey", + "valkey-fips", + }, + } + + ctx := t.Context() + m, err := NewMapper(ctx) + if err != nil { + t.Fatalf("unexpected error creating mapper: %s", err) + } + + for img, wantResults := range testCases { + t.Run(img, func(t *testing.T) { + got, err := m.Map(img) + if err != nil { + t.Errorf("unexpected error mapping %s: %s", img, err) + } + + want := &Mapping{ + Image: img, + Results: wantResults, + } + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("unexpected mapping for %s:\n%s", img, diff) + } + }) + } + +} diff --git a/image-mapper/internal/mapper/repos.go b/image-mapper/internal/mapper/repos.go new file mode 100644 index 0000000..fabbc2b --- /dev/null +++ b/image-mapper/internal/mapper/repos.go @@ -0,0 +1,321 @@ +package mapper + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" +) + +// Repo describes a repo in the catalog +type Repo struct { + Name string `json:"name"` + CatalogTier string `json:"catalogTier"` + Aliases []string `json:"aliases"` +} + +func listRepos(ctx context.Context) ([]Repo, error) { + c := &http.Client{} + + buf := bytes.NewReader([]byte(`{"query":"query OrganizationImageCatalog($organization: ID!) {\n repos(filter: {uidp: {childrenOf: $organization}}) {\n name\n aliases\n catalogTier\n }\n}","variables":{"excludeDates":true,"excludeEpochs":true,"organization":"ce2d1984a010471142503340d670612d63ffb9f6"}}`)) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://data.chainguard.dev/query?id=PrivateImageCatalog", buf) + if err != nil { + return nil, fmt.Errorf("constructing request: %w", err) + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("User-Agent", "image-mapper") + + resp, err := c.Do(req) + if err != nil { + return nil, fmt.Errorf("making request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + var data struct { + Data struct { + Repos []Repo `json:"repos"` + } `json:"data"` + } + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, fmt.Errorf("unmarshaling body: %w", err) + } + + return fixAliases(data.Data.Repos), nil +} + +// fixAliases corrects some notoriously incorrect aliases in the repository +// data. Generally these are cases where we associate multiple images in the +// same 'family' with every image in the 'family'. +// +// Naturally, this should be fixed in the actual data but that's +// non-trivial to do at the moment. So, until such time, we'll do it here to +// improve the results in the short term. +func fixAliases(repos []Repo) []Repo { + for i, repo := range repos { + for name, aliases := range aliasesFixes { + if repo.Name != name { + continue + } + repos[i].Aliases = aliases + } + } + + return repos +} + +var aliasesFixes = map[string][]string{ + "argo-cli": { + "quay.io/argoproj/argocli", + }, + "argo-cli-fips": { + "quay.io/argoproj/argocli", + }, + "argo-events": { + "quay.io/argoproj/argo-events", + }, + "argo-events-fips": { + "quay.io/argoproj/argo-events", + }, + "argo-exec": { + "quay.io/argoproj/argoexec", + }, + "argo-exec-fips": { + "quay.io/argoproj/argoexec", + }, + "argo-workflowcontroller": { + "quay.io/argoproj/workflow-controller", + }, + "argo-workflowcontroller-fips": { + "quay.io/argoproj/workflow-controller", + }, + "crossplane-aws": { + "ghcr.io/crossplane-contrib/provider-family-aws", + }, + "crossplane-aws-cloudformation": { + "ghcr.io/crossplane-contrib/provider-aws-cloudformation", + }, + "crossplane-aws-cloudformation-fips": { + "ghcr.io/crossplane-contrib/provider-aws-cloudformation", + }, + "crossplane-aws-cloudfront": { + "ghcr.io/crossplane-contrib/provider-aws-cloudfront", + }, + "crossplane-aws-cloudfront-fips": { + "ghcr.io/crossplane-contrib/provider-aws-cloudfront", + }, + "crossplane-aws-cloudwatchlogs": { + "ghcr.io/crossplane-contrib/provider-aws-cloudwatchlogs", + }, + "crossplane-aws-cloudwatchlogs-fips": { + "ghcr.io/crossplane-contrib/provider-aws-cloudwatchlogs", + }, + "crossplane-aws-dynamodb": { + "ghcr.io/crossplane-contrib/provider-aws-dynamodb", + }, + "crossplane-aws-dynamodb-fips": { + "ghcr.io/crossplane-contrib/provider-aws-dynamodb", + }, + "crossplane-aws-ec2": { + "ghcr.io/crossplane-contrib/provider-aws-ec2", + }, + "crossplane-aws-ec2-fips": { + "ghcr.io/crossplane-contrib/provider-aws-ec2", + }, + "crossplane-aws-eks": { + "ghcr.io/crossplane-contrib/provider-aws-eks", + }, + "crossplane-aws-eks-fips": { + "ghcr.io/crossplane-contrib/provider-aws-eks", + }, + "crossplane-aws-fips": { + "ghcr.io/crossplane-contrib/provider-family-aws", + }, + "crossplane-aws-firehose": { + "ghcr.io/crossplane-contrib/provider-aws-firehose", + }, + "crossplane-aws-firehose-fips": { + "ghcr.io/crossplane-contrib/provider-aws-firehose", + }, + "crossplane-aws-iam": { + "ghcr.io/crossplane-contrib/provider-aws-iam", + }, + "crossplane-aws-iam-fips": { + "ghcr.io/crossplane-contrib/provider-aws-iam", + }, + "crossplane-aws-kinesis": { + "ghcr.io/crossplane-contrib/provider-aws-kinesis", + }, + "crossplane-aws-kinesis-fips": { + "ghcr.io/crossplane-contrib/provider-aws-kinesis", + }, + "crossplane-aws-kms": { + "ghcr.io/crossplane-contrib/provider-aws-kms", + }, + "crossplane-aws-kms-fips": { + "ghcr.io/crossplane-contrib/provider-aws-kms", + }, + "crossplane-aws-lambda": { + "ghcr.io/crossplane-contrib/provider-aws-lambda", + }, + "crossplane-aws-lambda-fips": { + "ghcr.io/crossplane-contrib/provider-aws-lambda", + }, + "crossplane-aws-rds": { + "ghcr.io/crossplane-contrib/provider-aws-rds", + }, + "crossplane-aws-rds-fips": { + "ghcr.io/crossplane-contrib/provider-aws-rds", + }, + "crossplane-aws-route53": { + "ghcr.io/crossplane-contrib/provider-aws-route53", + }, + "crossplane-aws-route53-fips": { + "ghcr.io/crossplane-contrib/provider-aws-route53", + }, + "crossplane-aws-s3": { + "ghcr.io/crossplane-contrib/provider-aws-s3", + }, + "crossplane-aws-s3-fips": { + "ghcr.io/crossplane-contrib/provider-aws-s3", + }, + "crossplane-aws-sns": { + "ghcr.io/crossplane-contrib/provider-aws-sns", + }, + "crossplane-aws-sns-fips": { + "ghcr.io/crossplane-contrib/provider-aws-sns", + }, + "crossplane-aws-sqs": { + "ghcr.io/crossplane-contrib/provider-aws-sqs", + }, + "crossplane-aws-sqs-fips": { + "ghcr.io/crossplane-contrib/provider-aws-sqs", + }, + "cert-manager-acmesolver": { + "quay.io/jetstack/cert-manager-acmesolver", + }, + "cert-manager-acmesolver-fips": { + "quay.io/jetstack/cert-manager-acmesolver", + }, + "cert-manager-acmesolver-iamguarded": { + "quay.io/jetstack/cert-manager-acmesolver", + }, + "cert-manager-acmesolver-iamguarded-fips": { + "quay.io/jetstack/cert-manager-acmesolver", + }, + "cert-manager-cainjector": { + "quay.io/jetstack/cert-manager-cainjector", + }, + "cert-manager-cainjector-fips": { + "quay.io/jetstack/cert-manager-cainjector", + }, + "cert-manager-cainjector-iamguarded": { + "quay.io/jetstack/cert-manager-cainjector", + }, + "cert-manager-cainjector-iamguarded-fips": { + "quay.io/jetstack/cert-manager-cainjector", + }, + "cert-manager-cmctl": { + "quay.io/jetstack/cmctl", + }, + "cert-manager-cmctl-fips": { + "quay.io/jetstack/cmctl", + }, + "cert-manager-webhook": { + "quay.io/jetstack/cert-manager-webhook", + }, + "cert-manager-webhook-fips": { + "quay.io/jetstack/cert-manager-webhook", + }, + "cert-manager-webhook-iamguarded": { + "quay.io/jetstack/cert-manager-webhook", + }, + "cert-manager-webhook-iamguarded-fips": { + "quay.io/jetstack/cert-manager-webhook", + }, + "flux": { + "ghcr.io/fluxcd/flux-cli", + }, + "flux-fips": { + "ghcr.io/fluxcd/flux-cli", + }, + "flux-helm-controller": { + "ghcr.io/fluxcd/helm-controller", + }, + "flux-helm-controller-fips": { + "ghcr.io/fluxcd/helm-controller", + }, + "flux-image-automation-controller": { + "ghcr.io/fluxcd/image-automation-controller", + }, + "flux-image-automation-controller-fips": { + "ghcr.io/fluxcd/image-automation-controller", + }, + "flux-image-reflector-controller": { + "ghcr.io/fluxcd/image-reflector-controller", + }, + "flux-image-reflector-controller-fips": { + "ghcr.io/fluxcd/image-reflector-controller", + }, + "flux-kustomize-controller": { + "ghcr.io/fluxcd/kustomize-controller", + }, + "flux-kustomize-controller-fips": { + "ghcr.io/fluxcd/kustomize-controller", + }, + "flux-notification-controller": { + "ghcr.io/fluxcd/notification-controller", + }, + "flux-notification-controller-fips": { + "ghcr.io/fluxcd/notification-controller", + }, + "flux-source-controller": { + "ghcr.io/fluxcd/source-controller", + }, + "flux-source-controller-fips": { + "ghcr.io/fluxcd/source-controller", + }, + "minio-client": { + "quay.io/minio/mc", + }, + "minio-client-fips": { + "quay.io/minio/mc", + }, + "minio-operator": { + "quay.io/minio/operator", + }, + "minio-operator-fips": { + "quay.io/minio/operator", + }, + "minio-operator-sidecar": { + "quay.io/minio/operator-sidecar", + }, + "minio-operator-sidecar-fips": { + "quay.io/minio/operator-sidecar", + }, + "mongodb-kubernetes-operator-readinessprobe": { + "quay.io/mongodb/mongodb-kubernetes-readinessprobe", + }, + "mongodb-kubernetes-operator-readinessprobe-fips": { + "quay.io/mongodb/mongodb-kubernetes-readinessprobe", + }, + "mongodb-kubernetes-operator-version-upgrade-post-start-hook": { + "quay.io/mongodb/mongodb-kubernetes-operator-version-upgrade-post-start-hook", + }, + "mongodb-kubernetes-operator-version-upgrade-post-start-hook-fips": { + "quay.io/mongodb/mongodb-kubernetes-operator-version-upgrade-post-start-hook", + }, + "postgres-cloudnative-pg": { + "ghcr.io/cloudnative-pg/postgresql", + }, + "postgres-cloudnative-pg-fips": { + "ghcr.io/cloudnative-pg/postgresql", + }, + "vault-k8s": { + "hashicorp/vault-k8s", + }, +}