Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 6
HYPERFLEET-1412 - feat: Support disconnected installs#19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -8,6 +8,7 @@ VERSION ?= 0.0.1 | ||
| # Set the Operator SDK version to use. By default, what is installed on the system is used. | ||
| # This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. | ||
| OPERATOR_SDK_VERSION ?= v1.42.3 | ||
| YQ_VERSION ?= v4.44.1 | ||
| # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) | ||
| ifeq (,$(shell go env GOBIN)) | ||
| @@ -120,8 +121,12 @@ cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests | ||
| ##@ Lint | ||
| .PHONY: verify-related-images | ||
| verify-related-images: ## Verify immutable deployable images match CSV relatedImages. | ||
| go run ./hack/verify-related-images | ||
| .PHONY: lint | ||
| lint: ## Run golangci-lint linter | ||
| lint: verify-related-images ## Run image verification and golangci-lint. | ||
| $(GOLANGCI_LINT) run | ||
| .PHONY: lint-fix | ||
| @@ -134,6 +139,18 @@ lint-config: ## Verify golangci-lint linter configuration | ||
| ##@ Build | ||
| OC_MIRROR_IMAGE ?= hyperfleet-oc-mirror:local | ||
| .PHONY: build-oc-mirror-image | ||
| build-oc-mirror-image: ## Build the containerized oc-mirror runner. | ||
| $(CONTAINER_TOOL) build -f hack/oc-mirror.Dockerfile -t $(OC_MIRROR_IMAGE) . | ||
| .PHONY: test-disconnected-mirror | ||
| test-disconnected-mirror: ## Exercise the disk-to-mirror archive transfer. | ||
| CONTAINER_TOOL=$(CONTAINER_TOOL) OC_MIRROR_IMAGE=$(OC_MIRROR_IMAGE) \ | ||
| BUNDLE_IMAGE="$(BUNDLE_IMAGE)" OPERATOR_IMAGE="$(OPERATOR_IMAGE)" API_IMAGE="$(API_IMAGE)" \ | ||
| ./hack/test-disconnected-mirror.sh | ||
| .PHONY: build | ||
| build: manifests generate fmt vet ## Build manager binary. | ||
| go build -o bin/manager cmd/main.go | ||
| @@ -350,16 +367,18 @@ FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) | ||
| endif | ||
| .PHONY: bundle | ||
| bundle: manifests operator-sdk ## Generate bundle manifests and metadata, then validate generated files. | ||
| bundle: manifests operator-sdk yq ## Generate bundle manifests and metadata, then validate generated files. | ||
| $(OPERATOR_SDK) generate kustomize manifests -q | ||
| $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS) | ||
| HYPERFLEET_OPERATOR_IMAGE_PULLSPEC="$$($(YQ) eval '.spec.install.spec.deployments[].spec.template.spec.containers[] | select(.name == "manager") | .image' bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml)" CSV_FILE=bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml ./hack/bundle/add_operator_related_image.sh | ||
| $(OPERATOR_SDK) bundle validate ./bundle | ||
| .PHONY: bundle-override-img | ||
| bundle-override-img: manifests operator-sdk ## Generate bundle with IMG override, then restore kustomization.yaml | ||
| bundle-override-img: manifests operator-sdk yq ## Generate bundle with IMG override, then restore kustomization.yaml | ||
| $(OPERATOR_SDK) generate kustomize manifests -q | ||
| cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) | ||
| $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS) | ||
| HYPERFLEET_OPERATOR_IMAGE_PULLSPEC="$$($(YQ) eval '.spec.install.spec.deployments[].spec.template.spec.containers[] | select(.name == "manager") | .image' bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml)" CSV_FILE=bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml ./hack/bundle/add_operator_related_image.sh --allow-tag | ||
| $(OPERATOR_SDK) bundle validate ./bundle | ||
| @echo "Bundle generated with IMG=$(IMG)" | ||
| @echo "Note: config/manager/kustomization.yaml has been modified. Commit or reset as needed." | ||
| @@ -396,6 +415,23 @@ $(LOCALBIN): | ||
| KUBECTL ?= kubectl | ||
| KIND ?= kind | ||
| .PHONY: yq | ||
| YQ ?= $(LOCALBIN)/yq | ||
| yq: ## Download yq locally if necessary. | ||
| ifeq (,$(wildcard $(YQ))) | ||
| ifeq (, $(shell which yq 2>/dev/null)) | ||
| @{ \ | ||
| set -e ;\ | ||
| mkdir -p $(dir $(YQ)) ;\ | ||
| OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ | ||
| curl -sSLo $(YQ) https://github.com/mikefarah/yq/releases/download/$(YQ_VERSION)/yq_$${OS}_$${ARCH} ;\ | ||
| chmod +x $(YQ) ;\ | ||
Comment on lines
+427
to
+428
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bashset -eu
printf'%s\n''--- Makefile lines 400-445 ---'
sed -n '400,445p' Makefile
printf'%s\n''--- YQ definitions and uses ---'
rg -n -C 3 '(^|[[:space:]])YQ(_VERSION)?[[:space:]]*[:?+]?=|\\$\\(YQ\\)|yq_' Makefile
printf'%s\n''--- relevant Make targets ---'
rg -n -C 5 'install.*yq|yq|bundle|disconnected|verify-related' MakefileRepository: openshift-hyperfleet/hyperfleet-operator Length of output: 10228 Other (CWE-494): Download of Code Without Integrity Check Reachability: External · Exploitability: Difficult Verify the downloaded yq binary before execution. This recipe downloads As per path instructions, tool installation must use checksummed downloads. 🤖 Prompt for AI AgentsSource: Path instructions | ||
| } | ||
| else | ||
| YQ = $(shell which yq) | ||
| endif | ||
| endif | ||
| .PHONY: operator-sdk | ||
| OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk | ||
| operator-sdk: ## Download operator-sdk locally if necessary. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| # Konflux bundle image build. Unlike the auto-generated bundle.Dockerfile (used | ||
| # for local dev with operator-sdk), this runs bundle-hack/update_bundle.sh to | ||
| # for local dev with operator-sdk), this runs hack/bundle/update_bundle.sh to | ||
| # patch digest-pinned image references into the CSV at build time. | ||
| FROM registry.access.redhat.com/ubi9/ubi-minimal:latest AS builder-runner | ||
| RUN microdnf install -y tar gzip && \ | ||
| @@ -12,10 +12,10 @@ FROM builder-runner AS builder | ||
| ARG HYPERFLEET_OPERATOR_IMAGE_PULLSPEC="quay.io/redhat-user-workloads/hyperfleet-tenant/hyperfleet/hyperfleet-operator@sha256:31e365d312b6f3d483913d4029eba565abd64ab38a6d117658324afe225f708d" | ||
| ENV HYPERFLEET_OPERATOR_IMAGE_PULLSPEC=${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC} | ||
| ARG HYPERFLEET_API_IMAGE_PULLSPEC="quay.io/redhat-user-workloads/hyperfleet-tenant/hyperfleet/hyperfleet-api@sha256:8533d0d875480f31f5112e454659a095a5d2e993c139a9045a06be6b67b829ca" | ||
| ARG HYPERFLEET_API_IMAGE_PULLSPEC="quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api@sha256:8533d0d875480f31f5112e454659a095a5d2e993c139a9045a06be6b67b829ca" | ||
| ENV HYPERFLEET_API_IMAGE_PULLSPEC=${HYPERFLEET_API_IMAGE_PULLSPEC} | ||
| COPY bundle-hack . | ||
| COPY hack/bundle . | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this have a knock on effect that needs to be reflected in the bundle push job? I think that still points to bundle-hack | ||
| COPY bundle/manifests /manifests/ | ||
| RUN ./update_bundle.sh | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -33,7 +33,7 @@ metadata: | ||
| } | ||
| ] | ||
| capabilities: Basic Install | ||
| createdAt: "2026-09-01T18:11:57Z" | ||
| createdAt: "2026-09-03T22:24:44Z" | ||
| operators.operatorframework.io/builder: operator-sdk-v1.42.3 | ||
| operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 | ||
| name: hyperfleet-operator.v0.0.1 | ||
| @@ -169,8 +169,8 @@ spec: | ||
| fieldRef: | ||
| fieldPath: metadata.namespace | ||
| - name: RELATED_IMAGE_HYPERFLEET_API | ||
| value: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api:latest | ||
| image: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-operator:latest | ||
| value: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api@sha256:8533d0d875480f31f5112e454659a095a5d2e993c139a9045a06be6b67b829ca | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leave this as is I think | ||
| image: quay.io/redhat-user-workloads/hyperfleet-tenant/hyperfleet/hyperfleet-operator@sha256:31e365d312b6f3d483913d4029eba565abd64ab38a6d117658324afe225f708d | ||
| livenessProbe: | ||
| httpGet: | ||
| path: /healthz | ||
| @@ -266,6 +266,8 @@ spec: | ||
| name: Red Hat | ||
| url: https://github.com/openshift-hyperfleet | ||
| relatedImages: | ||
| - image: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api:latest | ||
| - image: quay.io/redhat-user-workloads/hyperfleet-tenant/hyperfleet/hyperfleet-operator@sha256:31e365d312b6f3d483913d4029eba565abd64ab38a6d117658324afe225f708d | ||
| name: hyperfleet-operator | ||
| - image: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api@sha256:8533d0d875480f31f5112e454659a095a5d2e993c139a9045a06be6b67b829ca | ||
| name: hyperfleet-api | ||
| version: 0.0.1 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # Source of truth for the images deployable by the bundle CSV. | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No this is not true, I left the api and operator image as latest, because they get updated in the konflux dockerfile There were 3 scenarios I was thinking of:
I don't think having another source of truth is a great idea. Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the bundle.Dockerfile gets autogenerated each time a user runs | ||
| images: | ||
| - name: hyperfleet-operator | ||
| image: quay.io/redhat-user-workloads/hyperfleet-tenant/hyperfleet/hyperfleet-operator@sha256:31e365d312b6f3d483913d4029eba565abd64ab38a6d117658324afe225f708d | ||
| - name: hyperfleet-api | ||
| image: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api@sha256:8533d0d875480f31f5112e454659a095a5d2e993c139a9045a06be6b67b829ca | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # Disconnected mirroring with oc-mirror v2 | ||
| This is the minimal disk-to-registry procedure for the HyperFleet Operator. | ||
| Use a released bundle, operator image, and API image identified by immutable | ||
| `@sha256:<64 lowercase hex characters>` pullspecs. | ||
| ## Prerequisites | ||
| * `oc-mirror` v2 on the connected and disconnected hosts | ||
| * read access to the source registries | ||
| * write access to the disconnected registry | ||
| * transfer media with enough space for the archive | ||
| * an auth file and registry trust configured on both hosts | ||
| Do not put credentials in the configuration file: | ||
| ```bash | ||
| export REGISTRY_AUTH_FILE=$HOME/.config/containers/auth.json | ||
| ``` | ||
| ## 1. Create one ImageSetConfiguration | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For mirroring, I would assume we want to mirror the catalog and operator within it, not specific images? That was my assumption | ||
| Set up the mirror workspace and copy [`docs/examples/imageset-config-standalone.yaml`](examples/imageset-config-standalone.yaml): | ||
| ```bash | ||
| export MIRROR_ROOT="$HOME/hyperfleet-mirror" | ||
| rm -rf "$MIRROR_ROOT" | ||
| mkdir -p "$MIRROR_ROOT" | ||
| cp docs/examples/imageset-config-standalone.yaml "$MIRROR_ROOT/imageset-config.yaml" | ||
| ``` | ||
| Edit `$MIRROR_ROOT/imageset-config.yaml` to replace its three example pullspecs | ||
| with the released bundle, operator, and API image digests. The file uses | ||
| `additionalImages` intentionally: it transfers exactly the three listed | ||
| artifacts and does not discover a catalog or CSV. | ||
| Run the image check before mirroring: | ||
| ```bash | ||
| make verify-related-images | ||
| ``` | ||
| It verifies this equality: | ||
| ```text | ||
| deployable-images.yaml == CSV manager image + RELATED_IMAGE_* values == CSV spec.relatedImages | ||
| ``` | ||
| ## 2. Mirror to disk on the connected host | ||
| ```bash | ||
| oc-mirror --v2 \ | ||
| --config "$MIRROR_ROOT/imageset-config.yaml" \ | ||
| file://"$MIRROR_ROOT/archive" | ||
| ``` | ||
| Keep the complete `archive` directory. Transfer it and the configuration to the | ||
| disconnected mirroring host using approved media: | ||
| ```bash | ||
| tar -C "$MIRROR_ROOT" \ | ||
| -czf /media/transfer/hyperfleet-mirror.tgz \ | ||
| archive imageset-config.yaml | ||
| mkdir -p /var/tmp/hyperfleet-mirror | ||
| tar -C /var/tmp/hyperfleet-mirror \ | ||
| -xzf /media/transfer/hyperfleet-mirror.tgz | ||
| ``` | ||
| ## 3. Mirror from disk into the disconnected registry | ||
| ```bash | ||
| export DESTINATION='mirror.example.com:8443/hyperfleet' | ||
| oc-mirror --v2 \ | ||
| --config /var/tmp/hyperfleet-mirror/imageset-config.yaml \ | ||
| --from file:///var/tmp/hyperfleet-mirror/archive \ | ||
| docker://"$DESTINATION" | ||
| ``` | ||
| Save the command output. Apply any generated mirror resources to the cluster | ||
| before installing the bundle, for example: | ||
| ```bash | ||
| CLUSTER_RESOURCES="$(find /var/tmp/hyperfleet-mirror \ | ||
| -type d -path '*/working-dir/cluster-resources' | head -1)" | ||
| oc apply -f "$CLUSTER_RESOURCES" | ||
| ``` | ||
| Use the mirrored bundle with the normal installation procedure. The | ||
| `additionalImages` configuration does not create a catalog. Do not manually | ||
| rewrite image digests. | ||
| ## Acceptance evidence | ||
| Exercise the connected disk export and disconnected import procedure once on an | ||
| isolated OpenShift cluster. Record the command output in the PR Test Plan, | ||
| including the cluster and OpenShift version, destination registry, mirrored | ||
| bundle digest, successful CSV/operator/API readiness, and runtime image IDs | ||
| ending in the expected digests. Do not include credentials or Secret values. | ||
| For a repeatable local transfer check, use the containerized oc-mirror runner: | ||
| ```bash | ||
| BUNDLE_IMAGE='quay.io/.../hyperfleet-operator-bundle@sha256:<bundle-digest>' \ | ||
| make test-disconnected-mirror | ||
| ``` | ||
| The runner is built from [`hack/oc-mirror.Dockerfile`](../hack/oc-mirror.Dockerfile) | ||
| and deliberately keeps oc-mirror in a container. This check exercises archive | ||
| transfer only; it is not a cluster installation framework. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # Standalone registry+v1 fallback. Replace every example with an authenticated | ||
| # released pullspec. additionalImages copies exactly these artifacts; it does | ||
| # not discover bundle relatedImages and does not create an OLM catalog. | ||
| apiVersion: mirror.openshift.io/v2alpha1 | ||
| kind: ImageSetConfiguration | ||
| mirror: | ||
| additionalImages: | ||
| - name: registry.example.com/hyperfleet/hyperfleet-operator-bundle@sha256:0000000000000000000000000000000000000000000000000000000000000000 | ||
| - name: registry.example.com/hyperfleet/hyperfleet-operator@sha256:0000000000000000000000000000000000000000000000000000000000000000 | ||
| - name: registry.example.com/hyperfleet/hyperfleet-api@sha256:0000000000000000000000000000000000000000000000000000000000000000 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
| CSV_FILE="${CSV_FILE:-bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml}" | ||
| image="${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC:-}" | ||
| allow_tag=false | ||
| case "${1:-}" in | ||
| "") ;; | ||
| --allow-tag) | ||
| allow_tag=true | ||
| ;; | ||
| *) | ||
| echo "usage: $0 [--allow-tag]" >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| if [[ "$image" =~ ^[^[:space:]]+@sha256:[0-9a-f]{64}$ ]]; then | ||
| : | ||
| elif [[ "$allow_tag" == true && "$image" =~ ^[^[:space:]@]+:[^[:space:]@/]+$ ]]; then | ||
| : | ||
| else | ||
| if [[ "$allow_tag" == true ]]; then | ||
| echo "error: HYPERFLEET_OPERATOR_IMAGE_PULLSPEC must be a sha256 digest pullspec or tagged pullspec" >&2 | ||
| else | ||
| echo "error: HYPERFLEET_OPERATOR_IMAGE_PULLSPEC must be a sha256 digest pullspec" >&2 | ||
| fi | ||
| exit 1 | ||
| fi | ||
| [[ -f "$CSV_FILE" ]] || { echo "error: CSV not found: $CSV_FILE" >&2; exit 1; } | ||
| # operator-sdk derives operand relatedImages from RELATED_IMAGE_* variables but | ||
| # does not include the manager image itself. Insert that one generated entry | ||
| # without reserializing the whole generated CSV. | ||
| if grep -q '^[[:space:]]*name: hyperfleet-operator$' "$CSV_FILE"; then | ||
| echo "error: CSV already contains a hyperfleet-operator relatedImages entry" >&2 | ||
| exit 1 | ||
| fi | ||
| if [[ "$(grep -c '^ relatedImages:$' "$CSV_FILE")" -ne 1 ]]; then | ||
| echo "error: expected exactly one spec.relatedImages block in $CSV_FILE" >&2 | ||
| exit 1 | ||
| fi | ||
| tmp="$(mktemp "${CSV_FILE}.XXXXXX")" | ||
| trap 'rm -f "$tmp"' EXIT | ||
| awk -v image="$image" ' | ||
| /^ relatedImages:$/ { | ||
| print " - image: " image | ||
| print " name: hyperfleet-operator" | ||
| next | ||
| } | ||
| { print } | ||
| ' "$CSV_FILE" >"$tmp" | ||
| mv "$tmp" "$CSV_FILE" | ||
| trap - EXIT |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: openshift-hyperfleet/hyperfleet-operator
Length of output: 13716
Injection (CWE-78): Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
Exploitability: Moderate
Quote and validate Make variables before shell execution.
CONTAINER_TOOLandOC_MIRROR_IMAGEcan be overridden through Make or the environment. Quote their expansions at lines 146 and 150, and validateCONTAINER_TOOLagainst an allowlist. Otherwise, shell metacharacters can execute commands. This is CWE-78.🤖 Prompt for AI Agents
Source: Path instructions