feat(make): add OCI image labels for operator, bundle, catalog#223
feat(make): add OCI image labels for operator, bundle, catalog#223aslakknutsen wants to merge 18 commits intonetworking-incubator:mainfrom
Conversation
Add a script and GitHub Actions workflow that automatically submits the OLM bundle to k8s-operatorhub/community-operators when a non-prerelease is published. The workflow triggers on release:published events, generates the bundle, then uses hack/publish_operatorhub.sh to clone the community-operators repo, copy the bundle, and open a PR via gh. Includes: - hack/publish_operatorhub.sh with --dry-run support - .github/workflows/operatorhub.yml (release:published trigger) - bundle/ci.yaml (semver-mode update graph) - hack/operatorhub-pr-template.md (PR body for community-operators) - make release.operatorhub target - RELEASE.md updated with OperatorHub step Closes #22 Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Run operator-test-playbooks (kiwi) against the staged bundle before OperatorHub publish and on PRs that touch bundle-related paths, using a pinned public controller image so PRs do not depend on unreleased digests. Add hack/operatorhub_opp_test.sh and make bundle.opp for local runs; align release workflow Python deps with the PR path, set CONTROLLER_MANAGER_CONTAINER_IMAGE to the release tag when generating the bundle, and print OPP logs on failure. Set maintainer email in the CSV template for OperatorHub metadata checks.
Unquoted semver (e.g. 0.0.0) can round-trip through YAML to JSON as a number. OLM OperatorVersion unmarshals only from JSON strings, so tools reported csv.Spec.Version unset. Emit spec.version as a quoted scalar. Signed-off-by: Aslak Knutsen <aslak@4fs.no>
The chart lives under charts/coraza-kubernetes-operator/, not helm/chart/. Align the workflow path filter so chart changes trigger the OPP kiwi job. Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Introduce KUBE_VERSION (default 1.33.0) for Helm --kube-version, CSV minKubeVersion via generate_bundle --min-kube-version, and OPP kind (KIND_KUBE_VERSION=v$(KUBE_VERSION) on bundle.opp). Remove minKubeVersion from the CSV template so the generator is the single writer; keep DEFAULT_MIN_KUBE_VERSION in sync with the Makefile default for CLI-only runs. Made-with: Cursor Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Aslak Knutsen <aslak.tux@gmail.com>
Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Use HTTP Basic (x-access-token) for git-over-HTTPS push; GitHub rejects the REST-style Authorization token header used via http.extraHeader. Redact http.extraHeader in dry-run logs so credentials are not echoed. Fail if the version directory already exists in the cloned community-operators tree unless --force is passed, avoiding silent overwrites of submissions. Made-with: Cursor Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Aslak Knutsen <aslak.tux@gmail.com>
Co-authored-by: Shane Utt <shane@shaneutt.com> Signed-off-by: Aslak Knutsen <aslak.tux@gmail.com>
Replace http.extraHeader Basic-auth push in publish_operatorhub.sh with plain git push after the workflow runs gh auth setup-git using OPERATORHUB_TOKEN. Simplify skip_in_dry_run now that the push line carries no secret. Document local gh auth setup in RELEASE.md. Made-with: Cursor Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Hand-maintained community-operators metadata lived under bundle/, which .gitignore treats as generated output. Move the source to bundle/base/ and copy it to bundle/ci.yaml during make bundle so regeneration cannot drop or confuse it. Document the layout in RELEASE.md and DEVELOPMENT.md. Made-with: Cursor Signed-off-by: Aslak Knutsen <aslak@4fs.no>
The community-operators checkout can already contain operators/<name>/ci.yaml. Overwrite it from bundle/ci.yaml on every publish so bundle/base/ci.yaml remains the source of truth for reviewers and updateGraph. Match behavior in operatorhub_opp_test.sh and document in RELEASE.md. Made-with: Cursor Signed-off-by: Aslak Knutsen <aslak@4fs.no>
Define org.opencontainers.image.* annotations (version, revision, created, source, documentation, licenses, vendor) and pass them via docker build --label on build.image, bundle.build, and catalog.build. Operator image includes distroless base name/digest matching the root Dockerfile. Bundle has no base labels (FROM scratch). Catalog includes opm base name and pinned manifest-list digest for OPM_VERSION. Resolves: networking-incubator#215 Made-with: Cursor Signed-off-by: Aslak Knutsen <aslak@4fs.no>
There was a problem hiding this comment.
Pull request overview
This PR adds OCI-standard org.opencontainers.image.* metadata to the container images produced by the repo’s build targets, and extends the OperatorHub/OLM release tooling to generate and validate bundles consistently (including min Kubernetes version and OperatorHub ci.yaml handling).
Changes:
- Add OCI image labels via
docker/podman build --labelfor operator, bundle, and catalog images. - Extend OLM/OperatorHub tooling: generate
bundle/ci.yaml, add scripts/workflows to run OPP tests and submit tocommunity-operators. - Align Kubernetes minimum version settings across bundle generation/Helm chart/docs.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
Makefile |
Adds OCI label sets and applies them to build targets; adds OperatorHub and OPP-related targets; introduces KUBE_VERSION. |
hack/lib.py |
Adds SemverYAML representer to ensure CSV spec.version is emitted as a quoted string. |
hack/generate_bundle.py |
Uses SemverYAML, sets CSV minKubeVersion from a flag/default, and copies bundle/base/ci.yaml to bundle/ci.yaml. |
hack/publish_operatorhub.sh |
New script to stage a bundle into a community-operators checkout and open a PR. |
hack/operatorhub_opp_test.sh |
New script to stage a community-operators tree and run OPP tier tests. |
hack/operatorhub-pr-template.md |
New PR body template used by the publish script. |
.github/workflows/operatorhub.yml |
New release-triggered workflow to generate bundle, run OPP, and submit to OperatorHub. |
.github/workflows/operatorhub-opp-pr.yml |
New PR workflow to run OPP kiwi tests on bundle-related changes. |
bundle/base/ci.yaml |
Adds OperatorHub ci.yaml source-of-truth for reviewers/update graph. |
bundle/base/csv-template.yaml |
Adjusts CSV template metadata (maintainer email; removes template minKubeVersion). |
charts/coraza-kubernetes-operator/Chart.yaml |
Lowers Helm chart kubeVersion constraint to >=1.32.0-0. |
README.md |
Updates stated supported Kubernetes version to v1.32+. |
RELEASE.md |
Documents OperatorHub workflow and new ci.yaml source-of-truth. |
DEVELOPMENT.md |
Documents bundle/base/ci.yaml and generated bundle/ci.yaml. |
.gitignore |
Unignores the new OperatorHub PR template; clarifies bundle generation comments. |
| .PHONY: release.operatorhub | ||
| release.operatorhub: ## Submit OLM bundle to OperatorHub community-operators | ||
| hack/publish_operatorhub.sh --version $(VERSION:v%=%) | ||
|
|
There was a problem hiding this comment.
The PR title/description focuses on adding OCI image labels, but this changeset also introduces OperatorHub submission/testing scripts + workflows and adjusts Kubernetes minimum version defaults (Makefile KUBE_VERSION, Chart kubeVersion, README). Please either update the PR description to cover these additional behavioral/process changes or split them into separate PRs so the scope matches the stated intent.
| BRANCH="${OPERATOR_NAME}-${VERSION}" | ||
|
|
||
| TMP_DIR="$(mktemp -d -t "${OPERATOR_NAME}.XXXXXXXXXX")" | ||
| skip_in_dry_run trap 'rm -rf -- "${TMP_DIR}"' EXIT |
There was a problem hiding this comment.
--dry-run currently wraps the trap setup in skip_in_dry_run, so the EXIT trap is never installed during dry runs. That leaves the mktemp directory behind whenever --dry-run is used. Set the cleanup trap unconditionally (and only gate the network/PR actions behind dry-run).
| skip_in_dry_run trap 'rm -rf -- "${TMP_DIR}"' EXIT | |
| trap 'rm -rf -- "${TMP_DIR}"' EXIT |
| if [[ -d "${OPERATORS_DIR}" && "${FORCE}" != true ]]; then | ||
| echo "Error: ${OPERATORS_DIR} already exists in ${OWNER}/${OPERATOR_HUB} (this version may already be submitted)." >&2 | ||
| echo "Refusing to overwrite. Use --force only if you intend to replace that tree." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| mkdir -p "${OPERATORS_DIR}/manifests" | ||
| mkdir -p "${OPERATORS_DIR}/metadata" | ||
| cp -a "${BUNDLE_MANIFESTS}/." "${OPERATORS_DIR}/manifests/" | ||
| cp -a "${BUNDLE_METADATA}/." "${OPERATORS_DIR}/metadata/" |
There was a problem hiding this comment.
When --force is used and operators/<name>/<version> already exists, the script overwrites/merges files with cp -a but never removes files that were deleted from the new bundle. This can leave stale manifests/metadata in the submission. In --force mode, delete the existing ${OPERATORS_DIR} tree (or otherwise sync with deletion) before copying the new bundle content.
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" | ||
|
|
||
| OPP_SCRIPT_URL="https://raw.githubusercontent.com/redhat-openshift-ecosystem/community-operators-pipeline/ci/latest/ci/scripts/opp.sh" |
There was a problem hiding this comment.
The script comment says the OPP pipeline is pinned to a specific commit, but OPP_SCRIPT_URL is pointing at .../ci/latest/.../opp.sh, which can change over time and make CI results non-reproducible. Point this URL at the intended pinned commit (or remove the pinning claim).
| OPP_SCRIPT_URL="https://raw.githubusercontent.com/redhat-openshift-ecosystem/community-operators-pipeline/ci/latest/ci/scripts/opp.sh" | |
| OPP_SCRIPT_URL="https://raw.githubusercontent.com/redhat-openshift-ecosystem/community-operators-pipeline/fbac22ff0c713188bdcea36791b70fb3999e6e03/ci/scripts/opp.sh" |
|
|
||
| # Align with community OperatorHub k8s pipeline (see maistra istio-workspace test.sh). | ||
| export OPP_CONTAINER_OPT="${OPP_CONTAINER_OPT:--t}" | ||
| export OPP_IMAGE="${OPP_IMAGE:-quay.io/operator_testing/operator-test-playbooks:latest}" |
There was a problem hiding this comment.
OPP_IMAGE defaults to quay.io/operator_testing/operator-test-playbooks:latest. Since this script is used in GitHub Actions, using :latest can introduce flaky/irreproducible CI when the upstream tag moves. Prefer pinning to a specific version tag or digest and bump it intentionally.
| export OPP_IMAGE="${OPP_IMAGE:-quay.io/operator_testing/operator-test-playbooks:latest}" | |
| export OPP_IMAGE="${OPP_IMAGE:-quay.io/operator_testing/operator-test-playbooks:v1.0.0}" |
| # Base image for the catalog final stage (see catalog/Dockerfile). When bumping OPM_VERSION, refresh | ||
| # OPM_BASE_DIGEST to the manifest list digest for that tag (e.g. quay.io API or skopeo inspect). | ||
| OPM_BASE_NAME ?= quay.io/operator-framework/opm:$(OPM_VERSION) | ||
| OPM_BASE_DIGEST ?= sha256:a070b901663d00312ccabe06a3c04b961d7326868499fe6c4c6b97025c79f014 |
There was a problem hiding this comment.
OPM_BASE_DIGEST is used to set org.opencontainers.image.base.digest for the catalog image, but catalog/Dockerfile currently uses FROM quay.io/operator-framework/opm:${OPM_VERSION} (tag-only) and will pull a platform-specific manifest that may not match this hardcoded manifest-list digest (and could drift if the tag ever changes). To keep the label accurate, either pin the base image by digest in catalog/Dockerfile (and label that digest) or drop the base.digest label for the catalog build.
| # Base image for the catalog final stage (see catalog/Dockerfile). When bumping OPM_VERSION, refresh | |
| # OPM_BASE_DIGEST to the manifest list digest for that tag (e.g. quay.io API or skopeo inspect). | |
| OPM_BASE_NAME ?= quay.io/operator-framework/opm:$(OPM_VERSION) | |
| OPM_BASE_DIGEST ?= sha256:a070b901663d00312ccabe06a3c04b961d7326868499fe6c4c6b97025c79f014 | |
| # Base image for the catalog final stage (see catalog/Dockerfile). This is intentionally | |
| # referenced by tag only; we do not publish a fixed base digest for the catalog image | |
| # because the actual platform-specific image pulled by the tag may vary. | |
| OPM_BASE_NAME ?= quay.io/operator-framework/opm:$(OPM_VERSION) |
Describe the pull request
Define org.opencontainers.image.* annotations (version, revision, created,
source, documentation, licenses, vendor) and pass them via docker build
--label on build.image, bundle.build, and catalog.build.
Operator image includes distroless base name/digest matching the root
Dockerfile. Bundle has no base labels (FROM scratch). Catalog includes
opm base name and pinned manifest-list digest for OPM_VERSION.
Which issue this resolves
Resolves #215
Additional context
Must be rebased/merged after #199