From d6f4dff8b7c3dc8f0ab1c6ae3cedcfefcb3915b8 Mon Sep 17 00:00:00 2001 From: Mallory Hill Date: Wed, 26 Aug 2026 12:35:26 -0400 Subject: [PATCH 1/4] HYPERFLEET-1411 - feat: Add hyperfleet-operator image and bundle components --- .gitignore | 1 + Makefile | 264 ++++++++-------- bundle-hack/update_bundle.sh | 22 ++ bundle.Dockerfile | 21 ++ bundle.konflux.Dockerfile | 36 +++ ...er-manager-metrics-service_v1_service.yaml | 20 ++ ...c.authorization.k8s.io_v1_clusterrole.yaml | 21 ++ ...c.authorization.k8s.io_v1_clusterrole.yaml | 27 ++ ...c.authorization.k8s.io_v1_clusterrole.yaml | 23 ++ ...c.authorization.k8s.io_v1_clusterrole.yaml | 13 + ...rfleet-operator.clusterserviceversion.yaml | 264 ++++++++++++++++ ...perfleet.redhat.com_hyperfleetconfigs.yaml | 281 ++++++++++++++++++ bundle/metadata/annotations.yaml | 15 + bundle/tests/scorecard/config.yaml | 70 +++++ config/manager/kustomization.yaml | 21 ++ config/manager/manager.yaml | 2 +- ...rfleet-operator.clusterserviceversion.yaml | 81 +++++ 17 files changed, 1062 insertions(+), 120 deletions(-) create mode 100755 bundle-hack/update_bundle.sh create mode 100644 bundle.Dockerfile create mode 100644 bundle.konflux.Dockerfile create mode 100644 bundle/manifests/hyperfleet-operator-controller-manager-metrics-service_v1_service.yaml create mode 100644 bundle/manifests/hyperfleet-operator-hyperfleetconfig-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml create mode 100644 bundle/manifests/hyperfleet-operator-hyperfleetconfig-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml create mode 100644 bundle/manifests/hyperfleet-operator-hyperfleetconfig-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml create mode 100644 bundle/manifests/hyperfleet-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml create mode 100644 bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml create mode 100644 bundle/manifests/hyperfleet.redhat.com_hyperfleetconfigs.yaml create mode 100644 bundle/metadata/annotations.yaml create mode 100644 bundle/tests/scorecard/config.yaml create mode 100644 config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml diff --git a/.gitignore b/.gitignore index 3513d3f..2b7fe6a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.so *.dylib bin/* +dist/ _output/ Dockerfile.cross diff --git a/Makefile b/Makefile index 35af4c3..1d6fcc2 100644 --- a/Makefile +++ b/Makefile @@ -5,47 +5,6 @@ # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) VERSION ?= 0.0.1 -# CHANNELS define the bundle channels used in the bundle. -# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") -# To re-generate a bundle for other specific channels without changing the standard setup, you can: -# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) -# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") -ifneq ($(origin CHANNELS), undefined) -BUNDLE_CHANNELS := --channels=$(CHANNELS) -endif - -# DEFAULT_CHANNEL defines the default channel used in the bundle. -# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") -# To re-generate a bundle for any other default channel without changing the default setup, you can: -# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) -# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") -ifneq ($(origin DEFAULT_CHANNEL), undefined) -BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) -endif -BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) - -# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images. -# This variable is used to construct full image tags for bundle and catalog images. -# -# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both -# redhat.com/hyperfleet-operator-bundle:$VERSION and redhat.com/hyperfleet-operator-catalog:$VERSION. -IMAGE_TAG_BASE ?= redhat.com/hyperfleet-operator - -# BUNDLE_IMG defines the image:tag used for the bundle. -# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) -BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) - -# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command -BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) - -# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests -# You can enable this value if you would like to use SHA Based Digests -# To enable set flag to true -USE_IMAGE_DIGESTS ?= false -ifeq ($(USE_IMAGE_DIGESTS), true) - BUNDLE_GEN_FLAGS += --use-image-digests -endif - # 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 @@ -189,50 +148,17 @@ run: manifests generate fmt vet ## Run a controller from your host. OPERATOR_NAMESPACE=$(OPERATOR_NAMESPACE) go run ./cmd/main.go -# If you wish to build the manager image targeting other platforms you can use the --platform flag. -# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. -# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -.PHONY: docker-build -docker-build: ## Build docker image with the manager. - $(CONTAINER_TOOL) build -t ${IMG} . - -.PHONY: docker-push -docker-push: ## Push docker image with the manager. - $(CONTAINER_TOOL) push ${IMG} - -# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple -# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: -# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ -# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) -# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. -PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le -.PHONY: docker-buildx -docker-buildx: ## Build and push docker image for the manager for cross-platform support - # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile - sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - - $(CONTAINER_TOOL) buildx create --name hyperfleet-operator-builder - $(CONTAINER_TOOL) buildx use hyperfleet-operator-builder - - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . - - $(CONTAINER_TOOL) buildx rm hyperfleet-operator-builder - rm Dockerfile.cross - -.PHONY: build-installer -build-installer: manifests generate ## Generate a consolidated YAML with CRDs and deployment. - @mkdir -p dist - @cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - @$(KUSTOMIZE) build config/default > dist/install.yaml - - ##@ Container Images # Image configuration PLATFORM ?= linux/amd64 -IMG_REGISTRY ?= quay.io/openshift-hyperfleet +QUAY_REPO ?= openshift-hyperfleet +IMG_REGISTRY ?= quay.io/$(QUAY_REPO) IMG_NAME ?= hyperfleet-operator -IMG_TAG ?= $(APP_VERSION) +IMG_TAG ?= v0.0.1 IMG ?= $(IMG_REGISTRY)/$(IMG_NAME):$(IMG_TAG) + APP_VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "0.0.0-dev") GIT_SHA ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown") GIT_DIRTY ?= $(shell [ -z "$$(git status --porcelain 2>/dev/null)" ] || echo "-modified") @@ -281,14 +207,43 @@ image-push: check-container-tool ## Push container image to registry image-build-push: image-build image-push ## Build and push container image to registry .PHONY: image-dev -image-build-push-dev: ## Build and push dev image to dev Quay registry (requires QUAY_USER) +image-dev: ## Build and push dev image to dev Quay registry (requires QUAY_USER) ifeq ($(strip $(QUAY_USER)),) @echo "Error: QUAY_USER is not set" @echo "" @echo "Usage: QUAY_USER=myuser make image-dev" @exit 1 endif - IMG_REGISTRY=quay.io/$(QUAY_USER) IMG_TAG=$(DEV_TAG) $(MAKE) image-build-push + QUAY_REPO=$(QUAY_USER) IMG_TAG=$(DEV_TAG) $(MAKE) image-build-push + +# If you wish to build the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +.PHONY: docker-build +docker-build: ## Build docker image with the manager. + $(CONTAINER_TOOL) build -t ${IMG} . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + $(CONTAINER_TOOL) push ${IMG} + +# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ +# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) +# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name hyperfleet-operator-builder + $(CONTAINER_TOOL) buildx use hyperfleet-operator-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm hyperfleet-operator-builder + rm Dockerfile.cross + ##@ Deployment @@ -306,13 +261,122 @@ uninstall: manifests ## Uninstall CRDs from the K8s cluster specified in ~/.kube .PHONY: deploy deploy: manifests ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} @$(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - .PHONY: undeploy undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. @$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +##@ Bundles/Catalog + + +# Non-olm installs +# Generates dist/install.yaml +# Install resources +# kubectl apply -f dist/install.yaml +# Uninstall resources +# kubectl delete -f dist/install.yaml +# For image overrides edit config/manager/kustomization.yaml +.PHONY: build-installer +build-installer: manifests generate ## Generate a consolidated YAML with CRDs and deployment. + @mkdir -p dist + @$(KUSTOMIZE) build config/default > dist/install.yaml + +.PHONY: build-installer-override-img +build-installer-override-img: manifests generate ## Generate installer with IMG override, then restore kustomization.yaml + @mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) + @$(KUSTOMIZE) build config/default > dist/install.yaml + @echo "Installer generated with IMG=$(IMG)" + @echo "Note: config/manager/kustomization.yaml has been modified. Commit or reset as needed." + + +# CHANNELS define the bundle channels used in the bundle. +# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") +# To re-generate a bundle for other specific channels without changing the standard setup, you can: +# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) +# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") +CHANNELS ?= stable,alpha +BUNDLE_CHANNELS := --channels=$(CHANNELS) + +# DEFAULT_CHANNEL defines the default channel used in the bundle. +# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") +# To re-generate a bundle for any other default channel without changing the default setup, you can: +# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) +# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") +DEFAULT_CHANNEL ?= stable +BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) +BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) + + +# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests +# You can enable this value if you would like to use SHA Based Digests +# To enable set flag to true +USE_IMAGE_DIGESTS ?= false +ifeq ($(USE_IMAGE_DIGESTS), true) + BUNDLE_GEN_FLAGS += --use-image-digests +endif + +# Defines the base of the registry we use for `make bundle-build catalog-build catalog-push bundle-push` +# Defaults to quay.io/openshift-hyperfleet/hyperfleet-operator +# For dev: If QUAY_REPO is set quay.io//hyperfleet-operator +REG_REPO_BASE ?= $(IMG_REGISTRY)/$(IMG_NAME) + +# Image tag for the bundle +BUNDLE_IMG ?= $(REG_REPO_BASE)-bundle:v$(VERSION) + +# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command +BUNDLE_GEN_FLAGS ?= -q --version $(VERSION) $(BUNDLE_METADATA_OPTS) + +# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). +# These images MUST exist in a registry and be pull-able. +BUNDLE_IMGS ?= $(BUNDLE_IMG) + +# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). +CATALOG_IMG ?= $(REG_REPO_BASE)-catalog:v$(VERSION) + +# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. +ifneq ($(origin CATALOG_BASE_IMG), undefined) +FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) +endif + +.PHONY: bundle +bundle: manifests operator-sdk ## 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) + $(OPERATOR_SDK) bundle validate ./bundle + +.PHONY: bundle-override-img +bundle-override-img: manifests operator-sdk ## 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) + $(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." + +.PHONY: bundle-build +bundle-build: ## Build the bundle image. + $(CONTAINER_TOOL) build -f bundle.Dockerfile -t $(BUNDLE_IMG) . + +.PHONY: bundle-push +bundle-push: ## Push the bundle image. + $(MAKE) docker-push IMG=$(BUNDLE_IMG) + +# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. +# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: +# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator +.PHONY: catalog-build +catalog-build: opm ## Build a catalog image. + $(OPM) index add --container-tool $(CONTAINER_TOOL) --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) + +# Push the catalog image. +.PHONY: catalog-push +catalog-push: ## Push a catalog image. + $(MAKE) docker-push IMG=$(CATALOG_IMG) + + ##@ Dependencies ## Location to install dependencies to @@ -341,20 +405,6 @@ OPERATOR_SDK = $(shell which operator-sdk) endif endif -.PHONY: bundle -bundle: manifests operator-sdk ## Generate bundle manifests and metadata, then validate generated files. - $(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) - $(OPERATOR_SDK) bundle validate ./bundle - -.PHONY: bundle-build -bundle-build: ## Build the bundle image. - $(CONTAINER_TOOL) build -f bundle.Dockerfile -t $(BUNDLE_IMG) . - -.PHONY: bundle-push -bundle-push: ## Push the bundle image. - $(MAKE) docker-push IMG=$(BUNDLE_IMG) .PHONY: opm OPM = $(LOCALBIN)/opm @@ -372,27 +422,3 @@ else OPM = $(shell which opm) endif endif - -# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). -# These images MUST exist in a registry and be pull-able. -BUNDLE_IMGS ?= $(BUNDLE_IMG) - -# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). -CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) - -# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. -ifneq ($(origin CATALOG_BASE_IMG), undefined) -FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) -endif - -# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. -# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: -# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator -.PHONY: catalog-build -catalog-build: opm ## Build a catalog image. - $(OPM) index add --container-tool $(CONTAINER_TOOL) --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) - -# Push the catalog image. -.PHONY: catalog-push -catalog-push: ## Push a catalog image. - $(MAKE) docker-push IMG=$(CATALOG_IMG) diff --git a/bundle-hack/update_bundle.sh b/bundle-hack/update_bundle.sh new file mode 100755 index 0000000..3bb79a6 --- /dev/null +++ b/bundle-hack/update_bundle.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Konflux nudges update these variables with the latest digest-pinned pullspecs. + +HYPERFLEET_OPERATOR_IMAGE_PULLSPEC="${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC:-quay.io/openshift-hyperfleet/hyperfleet-operator:v0.0.1}" + +CSV_FILE="${CSV_FILE:-/manifests/hyperfleet-operator.clusterserviceversion.yaml}" + +# Update operator deployment image +yq eval ".spec.install.spec.deployments[].spec.template.spec.containers[] |= ( + select(.name == \"manager\") | + .image = \"${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC}\" +)" -i "${CSV_FILE}" + + +# Update containerImage annotation +yq eval ".metadata.annotations.containerImage = \"${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC}\"" -i "${CSV_FILE}" + +# Update relatedImages + +cat "${CSV_FILE}" diff --git a/bundle.Dockerfile b/bundle.Dockerfile new file mode 100644 index 0000000..09d1b08 --- /dev/null +++ b/bundle.Dockerfile @@ -0,0 +1,21 @@ +FROM scratch + +# Core bundle labels. +LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 +LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ +LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ +LABEL operators.operatorframework.io.bundle.package.v1=hyperfleet-operator +LABEL operators.operatorframework.io.bundle.channels.v1=stable,alpha +LABEL operators.operatorframework.io.bundle.channel.default.v1=stable +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.3 +LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 +LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 + +# Labels for testing. +LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 +LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ + +# Copy files to locations specified by labels. +COPY bundle/manifests /manifests/ +COPY bundle/metadata /metadata/ +COPY bundle/tests/scorecard /tests/scorecard/ diff --git a/bundle.konflux.Dockerfile b/bundle.konflux.Dockerfile new file mode 100644 index 0000000..3ac47ce --- /dev/null +++ b/bundle.konflux.Dockerfile @@ -0,0 +1,36 @@ +# 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 +# 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 && \ + curl -sL https://github.com/mikefarah/yq/releases/download/v4.44.1/yq_linux_amd64.tar.gz | tar xz && \ + mv yq_linux_amd64 /usr/bin/yq + +FROM builder-runner AS builder +# Hack to set the operator container image in the deployment +COPY bundle-hack . +COPY bundle/manifests /manifests/ + +RUN ./update_bundle.sh + +FROM scratch + +# Core bundle labels. +LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 +LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ +LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ +LABEL operators.operatorframework.io.bundle.package.v1=hyperfleet-operator +LABEL operators.operatorframework.io.bundle.channels.v1=stable,alpha +LABEL operators.operatorframework.io.bundle.channel.default.v1=stable +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.3 +LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 +LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 + +# Labels for testing. +LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 +LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ + +# Copy patched manifests from builder, metadata and tests from source. +COPY --from=builder /manifests /manifests/ +COPY bundle/metadata /metadata/ +COPY bundle/tests/scorecard /tests/scorecard/ diff --git a/bundle/manifests/hyperfleet-operator-controller-manager-metrics-service_v1_service.yaml b/bundle/manifests/hyperfleet-operator-controller-manager-metrics-service_v1_service.yaml new file mode 100644 index 0000000..b042de1 --- /dev/null +++ b/bundle/manifests/hyperfleet-operator-controller-manager-metrics-service_v1_service.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: hyperfleet-operator + control-plane: controller-manager + name: hyperfleet-operator-controller-manager-metrics-service +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: hyperfleet-operator + control-plane: controller-manager +status: + loadBalancer: {} diff --git a/bundle/manifests/hyperfleet-operator-hyperfleetconfig-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml b/bundle/manifests/hyperfleet-operator-hyperfleetconfig-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml new file mode 100644 index 0000000..99652ea --- /dev/null +++ b/bundle/manifests/hyperfleet-operator-hyperfleetconfig-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -0,0 +1,21 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: hyperfleet-operator + name: hyperfleet-operator-hyperfleetconfig-admin-role +rules: +- apiGroups: + - hyperfleet.redhat.com + resources: + - hyperfleetconfigs + verbs: + - '*' +- apiGroups: + - hyperfleet.redhat.com + resources: + - hyperfleetconfigs/status + verbs: + - get diff --git a/bundle/manifests/hyperfleet-operator-hyperfleetconfig-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml b/bundle/manifests/hyperfleet-operator-hyperfleetconfig-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml new file mode 100644 index 0000000..9ffd846 --- /dev/null +++ b/bundle/manifests/hyperfleet-operator-hyperfleetconfig-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -0,0 +1,27 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: hyperfleet-operator + name: hyperfleet-operator-hyperfleetconfig-editor-role +rules: +- apiGroups: + - hyperfleet.redhat.com + resources: + - hyperfleetconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - hyperfleet.redhat.com + resources: + - hyperfleetconfigs/status + verbs: + - get diff --git a/bundle/manifests/hyperfleet-operator-hyperfleetconfig-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml b/bundle/manifests/hyperfleet-operator-hyperfleetconfig-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml new file mode 100644 index 0000000..cf45968 --- /dev/null +++ b/bundle/manifests/hyperfleet-operator-hyperfleetconfig-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -0,0 +1,23 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: hyperfleet-operator + name: hyperfleet-operator-hyperfleetconfig-viewer-role +rules: +- apiGroups: + - hyperfleet.redhat.com + resources: + - hyperfleetconfigs + verbs: + - get + - list + - watch +- apiGroups: + - hyperfleet.redhat.com + resources: + - hyperfleetconfigs/status + verbs: + - get diff --git a/bundle/manifests/hyperfleet-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml b/bundle/manifests/hyperfleet-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml new file mode 100644 index 0000000..4450500 --- /dev/null +++ b/bundle/manifests/hyperfleet-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: hyperfleet-operator + name: hyperfleet-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get diff --git a/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml b/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml new file mode 100644 index 0000000..579ecb3 --- /dev/null +++ b/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml @@ -0,0 +1,264 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [ + { + "apiVersion": "hyperfleet.redhat.com/v1alpha1", + "kind": "HyperFleetConfig", + "metadata": { + "labels": { + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "hyperfleet-operator" + }, + "name": "cluster" + }, + "spec": { + "api": { + "auth": { + "audience": "hyperfleet-api", + "enabled": true, + "issuer": "https://issuer.example.com" + }, + "database": { + "secretRef": { + "name": "hyperfleet-db" + } + }, + "profile": "small" + }, + "bundle": "cloud-capi" + } + } + ] + capabilities: Full Lifecycle + createdAt: "2026-08-27T16:14:13Z" + operators.operatorframework.io/builder: operator-sdk-v1.42.3 + operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 + name: hyperfleet-operator.v0.0.1 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: |- + HyperFleetConfig is the Schema for the hyperfleetconfigs API. It is a + cluster-scoped singleton: exactly one instance, named "cluster", is permitted. + displayName: Hyper Fleet Config + kind: HyperFleetConfig + name: hyperfleetconfigs.hyperfleet.redhat.com + version: v1alpha1 + description: |- + Delivers HyperFleet as a standard Kubernetes operator, installed and + managed through OLM. It exposes a single cluster-scoped CR: HyperFleetConfig. + displayName: HyperFleet Operator + icon: + - base64data: "" + mediatype: "" + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + - serviceaccounts + - services + verbs: + - create + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - get + - list + - patch + - update + - watch + - apiGroups: + - hyperfleet.redhat.com + resources: + - hyperfleetconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - hyperfleet.redhat.com + resources: + - hyperfleetconfigs/finalizers + verbs: + - update + - apiGroups: + - hyperfleet.redhat.com + resources: + - hyperfleetconfigs/status + verbs: + - get + - patch + - update + - apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - get + - list + - patch + - update + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: hyperfleet-operator-controller-manager + deployments: + - label: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: hyperfleet-operator + control-plane: controller-manager + name: hyperfleet-operator-controller-manager + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: hyperfleet-operator + control-plane: controller-manager + strategy: {} + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + app.kubernetes.io/name: hyperfleet-operator + control-plane: controller-manager + spec: + containers: + - args: + - --metrics-bind-address=:8443 + - --leader-elect + - --health-probe-bind-address=:8081 + command: + - /manager + env: + - name: OPERATOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: RELATED_IMAGE_HYPERFLEET_API + value: quay.io/openshift-hyperfleet/hyperfleet-api@sha256:c2957324b525fbd7d5ec776f4a395783599e6a878fb3f5993edc06371f1579bd + image: quay.io/openshift-hyperfleet/hyperfleet-operator:placeholder + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + serviceAccountName: hyperfleet-operator-controller-manager + terminationGracePeriodSeconds: 10 + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: hyperfleet-operator-controller-manager + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - hyperfleet + - cluster-management + - multicluster + links: + - name: HyperFleet Operator + url: https://github.com/openshift-hyperfleet/hyperfleet-operator + maturity: alpha + minKubeVersion: 1.27.0 + provider: + name: Red Hat + url: https://github.com/openshift-hyperfleet + relatedImages: + - image: quay.io/openshift-hyperfleet/hyperfleet-api@sha256:c2957324b525fbd7d5ec776f4a395783599e6a878fb3f5993edc06371f1579bd + name: hyperfleet-api + version: 0.0.1 diff --git a/bundle/manifests/hyperfleet.redhat.com_hyperfleetconfigs.yaml b/bundle/manifests/hyperfleet.redhat.com_hyperfleetconfigs.yaml new file mode 100644 index 0000000..89778b9 --- /dev/null +++ b/bundle/manifests/hyperfleet.redhat.com_hyperfleetconfigs.yaml @@ -0,0 +1,281 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.21.0 + creationTimestamp: null + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: hyperfleet-operator + name: hyperfleetconfigs.hyperfleet.redhat.com +spec: + group: hyperfleet.redhat.com + names: + kind: HyperFleetConfig + listKind: HyperFleetConfigList + plural: hyperfleetconfigs + shortNames: + - hfc + singular: hyperfleetconfig + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.bundle + name: Bundle + type: string + - jsonPath: .spec.api.profile + name: Profile + type: string + - jsonPath: .status.conditions[?(@.type=="Available")].status + name: Available + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + HyperFleetConfig is the Schema for the hyperfleetconfigs API. It is a + cluster-scoped singleton: exactly one instance, named "cluster", is permitted. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + HyperFleetConfigSpec defines the desired state of HyperFleetConfig. It captures + partner intent only; internal machinery (broker, adapters, sentinel) is never + expressed here. + properties: + api: + description: api is the partner-facing configuration for the HyperFleet + API component. + properties: + auth: + description: auth configures partner-facing JWT authentication + intent. + properties: + audience: + description: |- + audience is the token audience the API requires. Required and non-empty + when enabled is true. + maxLength: 253 + minLength: 1 + type: string + enabled: + default: true + description: |- + enabled turns JWT authentication on for the API endpoint. It defaults to + true, so a config that omits it gets authentication ON. It is a pointer to + distinguish "unset" (apply the default, true) from an explicit false + (disable auth), which a non-pointer bool cannot express: with omitempty a + plain false is dropped and re-defaulted to true, so auth could never be + turned off via the typed client; without omitempty an unset field serializes + as false and suppresses the default. Only *bool avoids both traps. + type: boolean + issuer: + description: |- + issuer is the OIDC issuer URL that mints accepted tokens. Required when + enabled is true. Whenever it is set (regardless of enabled) it must be a + valid https URL with a host, so a malformed issuer is rejected at admission + rather than surfacing later at token-validation time. + maxLength: 2048 + minLength: 1 + type: string + x-kubernetes-validations: + - message: issuer must be a valid https URL + rule: isURL(self) && url(self).getScheme() == 'https' && + url(self).getHostname() != '' + type: object + x-kubernetes-validations: + - message: issuer and audience are required when auth is enabled + rule: '!self.enabled || (has(self.issuer) && has(self.audience))' + database: + description: database configures the external PostgreSQL connection. + properties: + secretRef: + description: |- + secretRef references a Secret holding the database connection credentials. + The Secret must provide the keys db.host, db.port, db.name, db.user and + db.password. + properties: + name: + description: |- + name is the name of the Secret in the operator's namespace. It must be a + valid DNS-1123 subdomain, matching what k8s.io/apimachinery/pkg/util/validation + enforces for Secret names (IsDNS1123Subdomain, max length 253), so an + unresolvable reference is rejected at admission rather than failing opaquely + when the reference is later resolved. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - secretRef + type: object + profile: + default: small + description: profile selects a sizing profile for the API. Defaults + to "small". + enum: + - small + - medium + - large + type: string + tls: + description: |- + tls optionally configures TLS for the API endpoint. When omitted, the + operator applies its default serving configuration. + properties: + secretRef: + description: |- + secretRef references a kubernetes.io/tls Secret (providing tls.crt and + tls.key) used to serve the API endpoint. + properties: + name: + description: |- + name is the name of the Secret in the operator's namespace. It must be a + valid DNS-1123 subdomain, matching what k8s.io/apimachinery/pkg/util/validation + enforces for Secret names (IsDNS1123Subdomain, max length 253), so an + unresolvable reference is rejected at admission rather than failing opaquely + when the reference is later resolved. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - secretRef + type: object + required: + - auth + - database + type: object + bundle: + description: |- + bundle selects one of the operator-internal bundle definitions. It is + immutable after creation: switching deployments requires recreating the + resource. + enum: + - cloud-capi + - onprem-agent + type: string + x-kubernetes-validations: + - message: bundle is immutable + rule: self == oldSelf + required: + - api + - bundle + type: object + status: + description: |- + HyperFleetConfigStatus defines the observed state of HyperFleetConfig. It is + populated by the bundle controller in later stories; this story defines the + schema only. + properties: + conditions: + description: |- + conditions represent the current installation health of the operand. + Recognized types are Available, Progressing and Degraded. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + observedGeneration: + description: observedGeneration is the .metadata.generation the operator + last acted on. + format: int64 + minimum: 0 + type: integer + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: the only permitted name is 'cluster'; HyperFleetConfig is a cluster-scoped + singleton + rule: self.metadata.name == 'cluster' + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml new file mode 100644 index 0000000..1cb10de --- /dev/null +++ b/bundle/metadata/annotations.yaml @@ -0,0 +1,15 @@ +annotations: + # Core bundle annotations. + operators.operatorframework.io.bundle.mediatype.v1: registry+v1 + operators.operatorframework.io.bundle.manifests.v1: manifests/ + operators.operatorframework.io.bundle.metadata.v1: metadata/ + operators.operatorframework.io.bundle.package.v1: hyperfleet-operator + operators.operatorframework.io.bundle.channels.v1: stable,alpha + operators.operatorframework.io.bundle.channel.default.v1: stable + operators.operatorframework.io.metrics.builder: operator-sdk-v1.42.3 + operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 + operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 + + # Annotations for testing. + operators.operatorframework.io.test.mediatype.v1: scorecard+v1 + operators.operatorframework.io.test.config.v1: tests/scorecard/ diff --git a/bundle/tests/scorecard/config.yaml b/bundle/tests/scorecard/config.yaml new file mode 100644 index 0000000..b2761e6 --- /dev/null +++ b/bundle/tests/scorecard/config.yaml @@ -0,0 +1,70 @@ +apiVersion: scorecard.operatorframework.io/v1alpha3 +kind: Configuration +metadata: + name: config +stages: +- parallel: true + tests: + - entrypoint: + - scorecard-test + - basic-check-spec + image: quay.io/operator-framework/scorecard-test:v1.42.3 + labels: + suite: basic + test: basic-check-spec-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-bundle-validation + image: quay.io/operator-framework/scorecard-test:v1.42.3 + labels: + suite: olm + test: olm-bundle-validation-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-crds-have-validation + image: quay.io/operator-framework/scorecard-test:v1.42.3 + labels: + suite: olm + test: olm-crds-have-validation-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-crds-have-resources + image: quay.io/operator-framework/scorecard-test:v1.42.3 + labels: + suite: olm + test: olm-crds-have-resources-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-spec-descriptors + image: quay.io/operator-framework/scorecard-test:v1.42.3 + labels: + suite: olm + test: olm-spec-descriptors-test + storage: + spec: + mountPath: {} + - entrypoint: + - scorecard-test + - olm-status-descriptors + image: quay.io/operator-framework/scorecard-test:v1.42.3 + labels: + suite: olm + test: olm-status-descriptors-test + storage: + spec: + mountPath: {} +storage: + spec: + mountPath: {} diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 5c5f0b8..4802452 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,2 +1,23 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + resources: - manager.yaml + +images: +- name: controller + newName: quay.io/openshift-hyperfleet/hyperfleet-operator + newTag: placeholder + +# Environment variables injected into the manager container +# These are used for related image references in the operator +patches: +- patch: |- + - op: add + path: /spec/template/spec/containers/0/env/- + value: + name: RELATED_IMAGE_HYPERFLEET_API + value: quay.io/openshift-hyperfleet/hyperfleet-api@sha256:c2957324b525fbd7d5ec776f4a395783599e6a878fb3f5993edc06371f1579bd + target: + kind: Deployment + name: controller-manager \ No newline at end of file diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 069e881..dcbcdb9 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -63,7 +63,7 @@ spec: args: - --leader-elect - --health-probe-bind-address=:8081 - image: controller:latest + image: controller name: manager env: # OPERATOR_NAMESPACE tells the operator which namespace to create operands diff --git a/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml b/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml new file mode 100644 index 0000000..47d6f07 --- /dev/null +++ b/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml @@ -0,0 +1,81 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [ + { + "apiVersion": "hyperfleet.redhat.com/v1alpha1", + "kind": "HyperFleetConfig", + "metadata": { + "labels": { + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "hyperfleet-operator" + }, + "name": "cluster" + }, + "spec": { + "api": { + "auth": { + "audience": "hyperfleet-api", + "enabled": true, + "issuer": "https://issuer.example.com" + }, + "database": { + "secretRef": { + "name": "hyperfleet-db" + } + }, + "profile": "small" + }, + "bundle": "cloud-capi" + } + } + ] + capabilities: Full Lifecycle + name: hyperfleet-operator.v0.0.0 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: |- + HyperFleetConfig is the Schema for the hyperfleetconfigs API. It is a + cluster-scoped singleton: exactly one instance, named "cluster", is permitted. + displayName: Hyper Fleet Config + kind: HyperFleetConfig + name: hyperfleetconfigs.hyperfleet.redhat.com + version: v1alpha1 + description: |- + Delivers HyperFleet as a standard Kubernetes operator, installed and + managed through OLM. It exposes a single cluster-scoped CR: HyperFleetConfig. + displayName: HyperFleet Operator + icon: + - base64data: "" + mediatype: "" + install: + spec: + deployments: null + strategy: "" + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - hyperfleet + - cluster-management + - multicluster + links: + - name: HyperFleet Operator + url: https://github.com/openshift-hyperfleet/hyperfleet-operator + maturity: alpha + minKubeVersion: 1.27.0 + provider: + name: Red Hat + url: https://github.com/openshift-hyperfleet + version: 0.0.0 From c6ebe8c15d2e1810ea0fb979da12f26545fba067 Mon Sep 17 00:00:00 2001 From: Mallory Hill Date: Thu, 27 Aug 2026 18:49:20 -0400 Subject: [PATCH 2/4] HYPERFLEET-1411 - feat: Add hyperfleet-operator image and bundle components --- Makefile | 7 ++++--- bundle.Dockerfile | 2 +- bundle.konflux.Dockerfile | 2 +- .../hyperfleet-operator.clusterserviceversion.yaml | 3 +-- bundle/metadata/annotations.yaml | 2 +- config/manager/kustomization.yaml | 2 +- .../bases/hyperfleet-operator.clusterserviceversion.yaml | 1 - 7 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 1d6fcc2..8be14e5 100644 --- a/Makefile +++ b/Makefile @@ -291,13 +291,13 @@ build-installer-override-img: manifests generate ## Generate installer with IMG @echo "Installer generated with IMG=$(IMG)" @echo "Note: config/manager/kustomization.yaml has been modified. Commit or reset as needed." - +# For now `stable` channel is the default and only channel # CHANNELS define the bundle channels used in the bundle. # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") # To re-generate a bundle for other specific channels without changing the standard setup, you can: # - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) # - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") -CHANNELS ?= stable,alpha +CHANNELS ?= stable BUNDLE_CHANNELS := --channels=$(CHANNELS) # DEFAULT_CHANNEL defines the default channel used in the bundle. @@ -327,7 +327,7 @@ REG_REPO_BASE ?= $(IMG_REGISTRY)/$(IMG_NAME) BUNDLE_IMG ?= $(REG_REPO_BASE)-bundle:v$(VERSION) # BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command -BUNDLE_GEN_FLAGS ?= -q --version $(VERSION) $(BUNDLE_METADATA_OPTS) +BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) # A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). # These images MUST exist in a registry and be pull-able. @@ -341,6 +341,7 @@ ifneq ($(origin CATALOG_BASE_IMG), undefined) FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) endif + .PHONY: bundle bundle: manifests operator-sdk ## Generate bundle manifests and metadata, then validate generated files. $(OPERATOR_SDK) generate kustomize manifests -q diff --git a/bundle.Dockerfile b/bundle.Dockerfile index 09d1b08..ed59506 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -5,7 +5,7 @@ LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=hyperfleet-operator -LABEL operators.operatorframework.io.bundle.channels.v1=stable,alpha +LABEL operators.operatorframework.io.bundle.channels.v1=stable LABEL operators.operatorframework.io.bundle.channel.default.v1=stable LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.3 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 diff --git a/bundle.konflux.Dockerfile b/bundle.konflux.Dockerfile index 3ac47ce..fc6470a 100644 --- a/bundle.konflux.Dockerfile +++ b/bundle.konflux.Dockerfile @@ -20,7 +20,7 @@ LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=hyperfleet-operator -LABEL operators.operatorframework.io.bundle.channels.v1=stable,alpha +LABEL operators.operatorframework.io.bundle.channels.v1=stable LABEL operators.operatorframework.io.bundle.channel.default.v1=stable LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.3 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 diff --git a/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml b/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml index 579ecb3..ad158e3 100644 --- a/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml +++ b/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml @@ -33,7 +33,7 @@ metadata: } ] capabilities: Full Lifecycle - createdAt: "2026-08-27T16:14:13Z" + createdAt: "2026-08-27T22:48:48Z" operators.operatorframework.io/builder: operator-sdk-v1.42.3 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 name: hyperfleet-operator.v0.0.1 @@ -253,7 +253,6 @@ spec: links: - name: HyperFleet Operator url: https://github.com/openshift-hyperfleet/hyperfleet-operator - maturity: alpha minKubeVersion: 1.27.0 provider: name: Red Hat diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml index 1cb10de..d1973b6 100644 --- a/bundle/metadata/annotations.yaml +++ b/bundle/metadata/annotations.yaml @@ -4,7 +4,7 @@ annotations: operators.operatorframework.io.bundle.manifests.v1: manifests/ operators.operatorframework.io.bundle.metadata.v1: metadata/ operators.operatorframework.io.bundle.package.v1: hyperfleet-operator - operators.operatorframework.io.bundle.channels.v1: stable,alpha + operators.operatorframework.io.bundle.channels.v1: stable operators.operatorframework.io.bundle.channel.default.v1: stable operators.operatorframework.io.metrics.builder: operator-sdk-v1.42.3 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 4802452..3bbe8f5 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -20,4 +20,4 @@ patches: value: quay.io/openshift-hyperfleet/hyperfleet-api@sha256:c2957324b525fbd7d5ec776f4a395783599e6a878fb3f5993edc06371f1579bd target: kind: Deployment - name: controller-manager \ No newline at end of file + name: controller-manager diff --git a/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml b/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml index 47d6f07..136eb6c 100644 --- a/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml @@ -73,7 +73,6 @@ spec: links: - name: HyperFleet Operator url: https://github.com/openshift-hyperfleet/hyperfleet-operator - maturity: alpha minKubeVersion: 1.27.0 provider: name: Red Hat From 22e023d2ff3f0f27c582098e8927380c16a50d24 Mon Sep 17 00:00:00 2001 From: Mallory Hill Date: Mon, 31 Aug 2026 14:30:26 -0400 Subject: [PATCH 3/4] HYPERFLEET-1411 - feat: Address comments from PR --- Dockerfile | 4 +- Makefile | 44 +++++---- README.md | 53 +--------- bundle-hack/update_bundle.sh | 8 +- bundle.konflux.Dockerfile | 1 + ...rfleet-operator.clusterserviceversion.yaml | 4 +- ...rfleet-operator.clusterserviceversion.yaml | 2 +- docs/bundle.md | 96 +++++++++++++++++++ 8 files changed, 132 insertions(+), 80 deletions(-) create mode 100644 docs/bundle.md diff --git a/Dockerfile b/Dockerfile index c98e765..80769c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,12 +23,14 @@ RUN CGO_ENABLED=1 GOEXPERIMENT=boringcrypto \ go build -trimpath -ldflags="-s -w" -o manager ./cmd/main.go # Runtime stage -FROM ${BASE_IMAGE} as final +FROM ${BASE_IMAGE} AS final WORKDIR / COPY --from=builder /workspace/manager . USER 65532:65532 +ARG APP_VERSION="0.0.0-dev" + ENTRYPOINT ["/manager"] LABEL name="hyperfleet-operator" \ diff --git a/Makefile b/Makefile index 8be14e5..f7e3301 100644 --- a/Makefile +++ b/Makefile @@ -174,11 +174,6 @@ GOFLAGS ?= -trimpath # -X github.com/openshift-hyperfleet/hyperfleet-operator/pkg/version.Commit=$(GIT_SHA) \ # -X 'github.com/openshift-hyperfleet/hyperfleet-operator/pkg/version.BuildTime=$(BUILD_DATE)' -# Dev image configuration - set QUAY_USER to push to personal registry -QUAY_USER ?= -DEV_TAG ?= dev-$(GIT_SHA) -BASE_IMAGE ?= registry.access.redhat.com/ubi9/ubi-minimal:latest - .PHONY: check-container-tool check-container-tool: ifndef CONTAINER_TOOL @@ -187,7 +182,7 @@ ifndef CONTAINER_TOOL endif .PHONY: image-build -image-build: check-container-tool manifests generate fmt vet ## Build container image with configurable registry/tag +image: check-container-tool manifests generate fmt vet ## Build container image with configurable registry/tag @echo "Building container image $(IMG)..." $(CONTAINER_TOOL) build \ --platform $(PLATFORM) \ @@ -204,17 +199,28 @@ image-push: check-container-tool ## Push container image to registry @echo "Image pushed: $(IMG)" .PHONY: image-build-push -image-build-push: image-build image-push ## Build and push container image to registry +image-build-push: image image-push ## Build and push container image to registry -.PHONY: image-dev -image-dev: ## Build and push dev image to dev Quay registry (requires QUAY_USER) +.PHONY: check-quay-user +check-quay-user: ifeq ($(strip $(QUAY_USER)),) @echo "Error: QUAY_USER is not set" @echo "" @echo "Usage: QUAY_USER=myuser make image-dev" @exit 1 endif - QUAY_REPO=$(QUAY_USER) IMG_TAG=$(DEV_TAG) $(MAKE) image-build-push + +# Usage: QUAY_USER=myuser make image-dev +# Dev image configuration - set QUAY_USER to push to personal registry +DEV_TAG ?= dev-$(GIT_SHA) +QUAY_USER ?= +DEV_BASE_IMAGE ?= registry.access.redhat.com/ubi9/ubi-minimal:latest + +.PHONY: image-dev +image-dev: QUAY_REPO = $(QUAY_USER) +image-dev: IMG_TAG = $(DEV_TAG) +image-dev: BASE_IMAGE = $(DEV_BASE_IMAGE) +image-dev: check-quay-user image-build-push ## Build and push dev image to dev Quay registry (requires QUAY_USER) # If you wish to build the manager image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. @@ -260,12 +266,13 @@ uninstall: manifests ## Uninstall CRDs from the K8s cluster specified in ~/.kube @$(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - .PHONY: deploy -deploy: manifests ## Deploy controller to the K8s cluster specified in ~/.kube/config. - @$(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - +deploy: ## Deploy controller to the K8s cluster specified in ~/.kube/config. + @test -f dist/install.yaml || { echo "Error: dist/install.yaml not found. Run 'make build-deployer or build-deployer-override-img' first."; exit 1; } + @$(KUBECTL) apply -f dist/install.yaml .PHONY: undeploy undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - @$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + @$(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f dist/install.yaml ##@ Bundles/Catalog @@ -278,17 +285,17 @@ undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/confi # Uninstall resources # kubectl delete -f dist/install.yaml # For image overrides edit config/manager/kustomization.yaml -.PHONY: build-installer -build-installer: manifests generate ## Generate a consolidated YAML with CRDs and deployment. +.PHONY: build-deployer +build-deployer: manifests generate ## Generate a consolidated YAML with CRDs and deployment. @mkdir -p dist @$(KUSTOMIZE) build config/default > dist/install.yaml -.PHONY: build-installer-override-img -build-installer-override-img: manifests generate ## Generate installer with IMG override, then restore kustomization.yaml +.PHONY: build-deployer-override-img +build-deployer-override-img: manifests generate ## Generate deployer with IMG override, then restore kustomization.yaml @mkdir -p dist cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) @$(KUSTOMIZE) build config/default > dist/install.yaml - @echo "Installer generated with IMG=$(IMG)" + @echo "Deployer generated with IMG=$(IMG)" @echo "Note: config/manager/kustomization.yaml has been modified. Commit or reset as needed." # For now `stable` channel is the default and only channel @@ -341,7 +348,6 @@ ifneq ($(origin CATALOG_BASE_IMG), undefined) FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) endif - .PHONY: bundle bundle: manifests operator-sdk ## Generate bundle manifests and metadata, then validate generated files. $(OPERATOR_SDK) generate kustomize manifests -q diff --git a/README.md b/README.md index 0488e88..da5a05e 100644 --- a/README.md +++ b/README.md @@ -62,63 +62,12 @@ kubectl delete -k config/samples/ make uninstall ``` -**UnDeploy the controller from the cluster:** +**Undeploy the controller from the cluster:** ```sh make undeploy ``` -## Project Distribution - -Following the options to release and provide this solution to the users. - -### By providing a bundle with all YAML files - -1. Build the installer for the image built and published in the registry: - -```sh -make build-installer IMG=/hyperfleet-operator:tag -``` - -**NOTE:** The makefile target mentioned above generates an 'install.yaml' -file in the dist directory. This file contains all the resources built -with Kustomize, which are necessary to install this project without its -dependencies. - -2. Using the installer - -Users can just run 'kubectl apply -f ' to install -the project, i.e.: - -```sh -kubectl apply -f https://raw.githubusercontent.com//hyperfleet-operator//dist/install.yaml -``` - -### By providing a Helm Chart - -1. Build the chart using the optional helm plugin - -```sh -operator-sdk edit --plugins=helm/v1-alpha -``` - -2. See that a chart was generated under 'dist/chart', and users -can obtain this solution from there. - -**NOTE:** If you change the project, you need to update the Helm Chart -using the same command above to sync the latest changes. Furthermore, -if you create webhooks, you need to use the above command with -the '--force' flag and manually ensure that any custom configuration -previously added to 'dist/chart/values.yaml' or 'dist/chart/manager/manager.yaml' -is manually re-applied afterwards. - -## Contributing -// TODO(user): Add detailed information on how you would like others to contribute to this project - -**NOTE:** Run `make help` for more information on all potential `make` targets - -More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) - ## License Copyright 2026. diff --git a/bundle-hack/update_bundle.sh b/bundle-hack/update_bundle.sh index 3bb79a6..990f231 100755 --- a/bundle-hack/update_bundle.sh +++ b/bundle-hack/update_bundle.sh @@ -3,20 +3,18 @@ set -euo pipefail # Konflux nudges update these variables with the latest digest-pinned pullspecs. -HYPERFLEET_OPERATOR_IMAGE_PULLSPEC="${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC:-quay.io/openshift-hyperfleet/hyperfleet-operator:v0.0.1}" +export HYPERFLEET_OPERATOR_IMAGE_PULLSPEC="${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC:-quay.io/openshift-hyperfleet/hyperfleet-operator:v0.0.1}" CSV_FILE="${CSV_FILE:-/manifests/hyperfleet-operator.clusterserviceversion.yaml}" # Update operator deployment image yq eval ".spec.install.spec.deployments[].spec.template.spec.containers[] |= ( select(.name == \"manager\") | - .image = \"${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC}\" + .image = strenv(HYPERFLEET_OPERATOR_IMAGE_PULLSPEC) )" -i "${CSV_FILE}" # Update containerImage annotation -yq eval ".metadata.annotations.containerImage = \"${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC}\"" -i "${CSV_FILE}" - -# Update relatedImages +yq eval ".metadata.annotations.containerImage = strenv(HYPERFLEET_OPERATOR_IMAGE_PULLSPEC)" -i "${CSV_FILE}" cat "${CSV_FILE}" diff --git a/bundle.konflux.Dockerfile b/bundle.konflux.Dockerfile index fc6470a..9d80bdc 100644 --- a/bundle.konflux.Dockerfile +++ b/bundle.konflux.Dockerfile @@ -8,6 +8,7 @@ RUN microdnf install -y tar gzip && \ FROM builder-runner AS builder # Hack to set the operator container image in the deployment + COPY bundle-hack . COPY bundle/manifests /manifests/ diff --git a/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml b/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml index ad158e3..5267400 100644 --- a/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml +++ b/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml @@ -32,8 +32,8 @@ metadata: } } ] - capabilities: Full Lifecycle - createdAt: "2026-08-27T22:48:48Z" + capabilities: Basic Install + createdAt: "2026-08-31T18:39:10Z" operators.operatorframework.io/builder: operator-sdk-v1.42.3 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 name: hyperfleet-operator.v0.0.1 diff --git a/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml b/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml index 136eb6c..905049c 100644 --- a/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/hyperfleet-operator.clusterserviceversion.yaml @@ -32,7 +32,7 @@ metadata: } } ] - capabilities: Full Lifecycle + capabilities: Basic Install name: hyperfleet-operator.v0.0.0 namespace: placeholder spec: diff --git a/docs/bundle.md b/docs/bundle.md new file mode 100644 index 0000000..b488dd5 --- /dev/null +++ b/docs/bundle.md @@ -0,0 +1,96 @@ +## Pre-merge checks +1. Updates to bundle.Dockerfile are also reflected in bundle.konflux.Dockerfile +2. bundle/ is correctly updated before merging +3. config/manager/kustomization.yaml is not wrongly updated + +## CI Installation + +Once Konflux is in place, the CI pipeline will automatically handle bundle updates: + +1. Konflux builds the operator image and publishes it with a new tag +2. `bundle.konflux.Dockerfile` runs `update_bundle.sh` with the new operator image tag +3. `update_bundle.sh` regenerates the bundle manifests with the updated operator image reference +4. The bundle is automatically built and published + +(TODO add more information once the konflux pipelines are in place) + +## Develpment Installation + +### Prerequisite steps +For local development and installation, set your Quay username to automatically configure image paths: + +```bash +# Export some variables to set image paths +export QUAY_USER= +export IMG_TAG= +git checkout -b # Checkout dev branch +make image-dev # Pushes to: quay.io/$QUAY_REPO/hyperfleet-operator: + +``` + + +### OLM Installation +Testing hyperfleet-operator installation with OLM + +**Image paths with QUAY_REPO set:** +- Operator: `quay.io/$QUAY_REPO/hyperfleet-operator:$DEV_TAG` +- Bundle: `quay.io/$QUAY_REPO/hyperfleet-operator-bundle:$VERSION` + +1. **Update bundle with operator image:** - WARNING restore changes once done testing! + ```bash + make bundle-override-img + # Updates bundle/ manifests with the operator image from step 2 + # Alternative: manually edit config/manager/kustomization.yaml + # Regenerates bundle.Dockerfile + bundle/ and override config/manager/kustomization.yaml + ``` + +2. **Build and push bundle image:** + ```bash + make bundle-build + make bundle-push + # Pushes to: quay.io/$QUAY_REPO/hyperfleet-operator-bundle: + ``` + +3. **Quick testing on a k8s cluster:** + ```bash + # Install Operator Lifecycle Manager in your cluster + operator-sdk olm install + + # Install operator from bundle + operator-sdk run bundle quay.io/$QUAY_REPO/hyperfleet-operator-bundle: -n + + # Cleanup when done + operator-sdk cleanup hyperfleet-operator -n + + # Uninstall Operator Lifecycle Manager from your cluster + operator-sdk olm uninstall + ``` + + +### Non-OLM Installation + +Testing hyperfleet-operator installation without OLM (kubectl apply) + +1. **Create dist/install.yaml with override image:** + ```bash + # Generate install.yaml with your custom image + make build-deployer-override-img + + # Alternative: manually edit config/manager/kustomization.yaml + # and run `make build-deployer` + # Generates: dist/install.yaml + # Again, make sure to restore config/manager/kustomization.yaml after testing + ``` + +2. **Quick testing on a k8s cluster:** + ```bash + make deploy + # Check status to see that everything installed properly + make undeploy + ``` + + +**Note:** `bundle-override-img` and `build-deployer-override-img` modify config/manager/kustomization.yaml in place. So before committing any chnages make sure to revert these changes. Addiitonally when running `bundle-override-img` the bundle/ and bundle.Dockerfile get regenerated in place, so make sure to check these changes before committing them. + + + From a89dc8f7e30b278cc1a49fafee1a052690ae4061 Mon Sep 17 00:00:00 2001 From: Mallory Hill Date: Mon, 31 Aug 2026 20:11:18 -0400 Subject: [PATCH 4/4] HYPERFLEET-1411 - feat: Updates to add in API image --- Dockerfile | 2 - Makefile | 11 +-- bundle-hack/update_bundle.sh | 26 +++--- bundle.konflux.Dockerfile | 14 +++ ...rfleet-operator.clusterserviceversion.yaml | 16 +++- ...perfleet.redhat.com_hyperfleetconfigs.yaml | 23 +++++ config/manager/kustomization.yaml | 27 ++++-- config/manager/manager.yaml | 12 +-- docs/bundle.md | 86 +++++++++++-------- 9 files changed, 141 insertions(+), 76 deletions(-) diff --git a/Dockerfile b/Dockerfile index 80769c5..439382c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,6 @@ ARG BASE_IMAGE=registry.access.redhat.com/ubi9-micro:latest FROM registry.access.redhat.com/ubi9/go-toolset:9.8-1786351949 AS builder -ARG APP_VERSION="0.0.0-dev" - USER root WORKDIR /workspace # Copy the Go Modules manifests diff --git a/Makefile b/Makefile index f7e3301..87fc497 100644 --- a/Makefile +++ b/Makefile @@ -155,9 +155,11 @@ PLATFORM ?= linux/amd64 QUAY_REPO ?= openshift-hyperfleet IMG_REGISTRY ?= quay.io/$(QUAY_REPO) IMG_NAME ?= hyperfleet-operator -IMG_TAG ?= v0.0.1 +IMG_TAG ?= $(APP_VERSION) IMG ?= $(IMG_REGISTRY)/$(IMG_NAME):$(IMG_TAG) - +# Base image for production builds - matches Dockerfile default +# Override with DEV_BASE_IMAGE for dev builds (see image-dev target) +BASE_IMAGE ?= registry.access.redhat.com/ubi9-micro:latest APP_VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "0.0.0-dev") GIT_SHA ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown") @@ -181,7 +183,7 @@ ifndef CONTAINER_TOOL @exit 1 endif -.PHONY: image-build +.PHONY: image image: check-container-tool manifests generate fmt vet ## Build container image with configurable registry/tag @echo "Building container image $(IMG)..." $(CONTAINER_TOOL) build \ @@ -266,8 +268,7 @@ uninstall: manifests ## Uninstall CRDs from the K8s cluster specified in ~/.kube @$(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - .PHONY: deploy -deploy: ## Deploy controller to the K8s cluster specified in ~/.kube/config. - @test -f dist/install.yaml || { echo "Error: dist/install.yaml not found. Run 'make build-deployer or build-deployer-override-img' first."; exit 1; } +deploy: build-deployer-override-img ## Deploy controller to the K8s cluster specified in ~/.kube/config. @$(KUBECTL) apply -f dist/install.yaml .PHONY: undeploy diff --git a/bundle-hack/update_bundle.sh b/bundle-hack/update_bundle.sh index 990f231..5f0ceed 100755 --- a/bundle-hack/update_bundle.sh +++ b/bundle-hack/update_bundle.sh @@ -1,20 +1,24 @@ #!/usr/bin/env bash set -euo pipefail -# Konflux nudges update these variables with the latest digest-pinned pullspecs. - -export HYPERFLEET_OPERATOR_IMAGE_PULLSPEC="${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC:-quay.io/openshift-hyperfleet/hyperfleet-operator:v0.0.1}" - CSV_FILE="${CSV_FILE:-/manifests/hyperfleet-operator.clusterserviceversion.yaml}" -# Update operator deployment image -yq eval ".spec.install.spec.deployments[].spec.template.spec.containers[] |= ( - select(.name == \"manager\") | - .image = strenv(HYPERFLEET_OPERATOR_IMAGE_PULLSPEC) -)" -i "${CSV_FILE}" +# Update image references in the CSV file using yq +yq eval ' + # Update operator deployment image + (.spec.install.spec.deployments[].spec.template.spec.containers[] | select(.name == "manager") | .image) = strenv(HYPERFLEET_OPERATOR_IMAGE_PULLSPEC) | + + # Update RELATED_IMAGE_HYPERFLEET_API env var + (.spec.install.spec.deployments[].spec.template.spec.containers[] | select(.name == "manager") | .env[] | select(.name == "RELATED_IMAGE_HYPERFLEET_API") | .value) = strenv(HYPERFLEET_API_IMAGE_PULLSPEC) | + # Update containerImage annotation + .metadata.annotations.containerImage = strenv(HYPERFLEET_OPERATOR_IMAGE_PULLSPEC) | -# Update containerImage annotation -yq eval ".metadata.annotations.containerImage = strenv(HYPERFLEET_OPERATOR_IMAGE_PULLSPEC)" -i "${CSV_FILE}" + # Update relatedImages + .spec.relatedImages = [ + {"name": "hyperfleet-operator", "image": strenv(HYPERFLEET_OPERATOR_IMAGE_PULLSPEC)}, + {"name": "hyperfleet-api", "image": strenv(HYPERFLEET_API_IMAGE_PULLSPEC)} + ] +' -i "${CSV_FILE}" cat "${CSV_FILE}" diff --git a/bundle.konflux.Dockerfile b/bundle.konflux.Dockerfile index 9d80bdc..a888235 100644 --- a/bundle.konflux.Dockerfile +++ b/bundle.konflux.Dockerfile @@ -8,6 +8,12 @@ RUN microdnf install -y tar gzip && \ FROM builder-runner AS builder # Hack to set the operator container image in the deployment +# Konflux nudges update these variables with the latest digest-pinned pullspecs. +ARG HYPERFLEET_OPERATOR_IMAGE_PULLSPEC="quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-operator@sha256:placeholder" +ENV HYPERFLEET_OPERATOR_IMAGE_PULLSPEC=${HYPERFLEET_OPERATOR_IMAGE_PULLSPEC} + +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 bundle/manifests /manifests/ @@ -35,3 +41,11 @@ LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ COPY --from=builder /manifests /manifests/ COPY bundle/metadata /metadata/ COPY bundle/tests/scorecard /tests/scorecard/ + + +ARG APP_VERSION="0.0.0-dev" +LABEL name="hyperfleet-operator-bundle" \ + vendor="Red Hat, Inc." \ + version="${APP_VERSION}" \ + summary="OLM bundle for the HyperFleet Operator" \ + description="OLM bundle for the HyperFleet Operator, which installs and manages HyperFleet." diff --git a/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml b/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml index 5267400..2654e73 100644 --- a/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml +++ b/bundle/manifests/hyperfleet-operator.clusterserviceversion.yaml @@ -33,7 +33,7 @@ metadata: } ] capabilities: Basic Install - createdAt: "2026-08-31T18:39:10Z" + createdAt: "2026-09-01T18:11:57Z" 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/openshift-hyperfleet/hyperfleet-api@sha256:c2957324b525fbd7d5ec776f4a395783599e6a878fb3f5993edc06371f1579bd - image: quay.io/openshift-hyperfleet/hyperfleet-operator:placeholder + value: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api:latest + image: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-operator:latest livenessProbe: httpGet: path: /healthz @@ -235,6 +235,14 @@ spec: verbs: - create - patch + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch serviceAccountName: hyperfleet-operator-controller-manager strategy: deployment installModes: @@ -258,6 +266,6 @@ spec: name: Red Hat url: https://github.com/openshift-hyperfleet relatedImages: - - image: quay.io/openshift-hyperfleet/hyperfleet-api@sha256:c2957324b525fbd7d5ec776f4a395783599e6a878fb3f5993edc06371f1579bd + - image: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api:latest name: hyperfleet-api version: 0.0.1 diff --git a/bundle/manifests/hyperfleet.redhat.com_hyperfleetconfigs.yaml b/bundle/manifests/hyperfleet.redhat.com_hyperfleetconfigs.yaml index 89778b9..18a37a1 100644 --- a/bundle/manifests/hyperfleet.redhat.com_hyperfleetconfigs.yaml +++ b/bundle/manifests/hyperfleet.redhat.com_hyperfleetconfigs.yaml @@ -101,6 +101,29 @@ spec: - message: issuer must be a valid https URL rule: isURL(self) && url(self).getScheme() == 'https' && url(self).getHostname() != '' + jwkCertSecretRef: + description: |- + jwkCertSecretRef optionally references a Secret holding the JWKS document, + for air-gapped or private environments where the API cannot reach a JWKS + URL. The Secret must provide the key "jwks.json" containing a JSON Web Key + Set (the format the API parses; see HYPERFLEET-1408). When unset, the + operator derives the JWKS URL from the issuer via OIDC discovery + ({issuer}/.well-known/openid-configuration → jwks_uri). + properties: + name: + description: |- + name is the name of the Secret in the operator's namespace. It must be a + valid DNS-1123 subdomain, matching what k8s.io/apimachinery/pkg/util/validation + enforces for Secret names (IsDNS1123Subdomain, max length 253), so an + unresolvable reference is rejected at admission rather than failing opaquely + when the reference is later resolved. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object type: object x-kubernetes-validations: - message: issuer and audience are required when auth is enabled diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 3bbe8f5..93a768e 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -4,20 +4,37 @@ kind: Kustomization resources: - manager.yaml +# Base image configuration for local development +# For production/Konflux builds, digest-pinned images are set via bundle.konflux.Dockerfile ARG overrides +# +# Local development override: +# make bundle-override-img IMG=quay.io//hyperfleet-operator:dev- +# make build-deployer-override-img IMG=quay.io//hyperfleet-operator:dev- +# +# NOTE: These commands modify this file in place. Restore before committing: +# git checkout config/manager/kustomization.yaml images: - name: controller - newName: quay.io/openshift-hyperfleet/hyperfleet-operator - newTag: placeholder + newName: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-operator + newTag: latest -# Environment variables injected into the manager container -# These are used for related image references in the operator +# RELATED_IMAGE_HYPERFLEET_API environment variable +# Sets the image used for the API operand. +# +# IMPORTANT: Must stay in sync with api.DefaultImage in the operator code +# (v0.4.0+: config schema compatibility plus HYPERFLEET-1603 database *_FILE support) +# +# For local development with a custom API image, manually edit the value below +# or use yq to update programmatically, then restore before committing. +# +# Production/Konflux: Digest-pinned via bundle.konflux.Dockerfile ARG overrides patches: - patch: |- - op: add path: /spec/template/spec/containers/0/env/- value: name: RELATED_IMAGE_HYPERFLEET_API - value: quay.io/openshift-hyperfleet/hyperfleet-api@sha256:c2957324b525fbd7d5ec776f4a395783599e6a878fb3f5993edc06371f1579bd + value: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api:latest target: kind: Deployment name: controller-manager diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index dcbcdb9..a8dd370 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -63,7 +63,7 @@ spec: args: - --leader-elect - --health-probe-bind-address=:8081 - image: controller + image: controller:latest name: manager env: # OPERATOR_NAMESPACE tells the operator which namespace to create operands @@ -72,16 +72,6 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - # RELATED_IMAGE_HYPERFLEET_API is the image used for the API operand. - # Follows the OLM relatedImages convention so it can be digest-pinned at - # bundle build time; override per environment as needed. Must stay in - # sync with api.DefaultImage (0.4.0+: config schema compatibility plus - # HYPERFLEET-1603 database *_FILE support, published to the - # redhat-services-prod registry via Konflux Release, not - # openshift-hyperfleet which stopped receiving pushes after v0.2.1) — - # see that constant's comment for why. - - name: RELATED_IMAGE_HYPERFLEET_API - value: quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-api:0.4.0 ports: [] securityContext: allowPrivilegeEscalation: false diff --git a/docs/bundle.md b/docs/bundle.md index b488dd5..99587ba 100644 --- a/docs/bundle.md +++ b/docs/bundle.md @@ -5,36 +5,43 @@ ## CI Installation -Once Konflux is in place, the CI pipeline will automatically handle bundle updates: +Once Konflux is in place, the CI pipeline will automatically handle bundling building with operator image updates: -1. Konflux builds the operator image and publishes it with a new tag -2. `bundle.konflux.Dockerfile` runs `update_bundle.sh` with the new operator image tag -3. `update_bundle.sh` regenerates the bundle manifests with the updated operator image reference -4. The bundle is automatically built and published +1. Konflux builds the operator image and publishes it to quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-operator +2. The operator-bundle .tekton pipeline will be triggered by any update to the bundle.konflux.Dockerfile +2. `bundle.konflux.Dockerfile` runs `update_bundle.sh` with the new operator image reference +3. `update_bundle.sh` uses yq to update the CSV to ensure the operator deployment has proper values - image, relatedImages, annotations, etc. +4. Publishes the operator-bundle to quay.io/redhat-services-prod/hyperfleet-tenant/hyperfleet/hyperfleet-operator-bundle -(TODO add more information once the konflux pipelines are in place) +(HYPERFLEET-1411: TODO add more information once the konflux pipelines are in place) -## Develpment Installation +## Development Installation ### Prerequisite steps For local development and installation, set your Quay username to automatically configure image paths: ```bash -# Export some variables to set image paths +# Set your Quay username (required for image-dev) export QUAY_USER= -export IMG_TAG= -git checkout -b # Checkout dev branch -make image-dev # Pushes to: quay.io/$QUAY_REPO/hyperfleet-operator: - +# Checkout dev branch +git checkout -b + +# Build and push dev image +make image-dev +# With default values - pushes to: quay.io/$QUAY_USER/hyperfleet-operator:dev- +export IMG=quay.io/$QUAY_USER/hyperfleet-operator:dev- +# export IMG so that it can be properly picked up for bundle generation ``` +**Image path defaults:** +- IMG (hyperfleet-operator image): `quay.io/$QUAY_USER/hyperfleet-operator:dev-` (defaults `make image-dev`) +- BUNDLE_IMG (hyperfleet-operator-bundle): `quay.io/$QUAY_USER/hyperfleet-operator-bundle:v$(VERSION)` (default VERSION=0.0.1) + -### OLM Installation +### OLM Installation Testing hyperfleet-operator installation with OLM -**Image paths with QUAY_REPO set:** -- Operator: `quay.io/$QUAY_REPO/hyperfleet-operator:$DEV_TAG` -- Bundle: `quay.io/$QUAY_REPO/hyperfleet-operator-bundle:$VERSION` +**Note:** Ensure `IMG` is properly exported before running these commands 1. **Update bundle with operator image:** - WARNING restore changes once done testing! ```bash @@ -48,49 +55,52 @@ Testing hyperfleet-operator installation with OLM ```bash make bundle-build make bundle-push - # Pushes to: quay.io/$QUAY_REPO/hyperfleet-operator-bundle: + # Pushes to: quay.io/$QUAY_USER/hyperfleet-operator-bundle:v$(VERSION) + # To override: make bundle-build VERSION=0.0.2 BUNDLE_IMG= ``` 3. **Quick testing on a k8s cluster:** ```bash + export BUNDLE_IMG=quay.io/$QUAY_USER/hyperfleet-operator-bundle:v$(VERSION) # Install Operator Lifecycle Manager in your cluster operator-sdk olm install - # Install operator from bundle - operator-sdk run bundle quay.io/$QUAY_REPO/hyperfleet-operator-bundle: -n + # Install operator from bundle (note: bundle image uses v prefix) + operator-sdk run bundle $(BUNDLE_IMG) -n - # Cleanup when done + # Cleanup when done - IMPORTANT: Delete CRs before uninstalling operator + # 1. Export and delete the cluster-scoped HyperFleetConfig CR + kubectl get hyperfleetconfig -o yaml > hyperfleetconfig-backup.yaml + kubectl delete hyperfleetconfig --all + + # 2. Clean up operator (removes CRDs and controller) operator-sdk cleanup hyperfleet-operator -n - # Uninstall Operator Lifecycle Manager from your cluster + # 3. Uninstall Operator Lifecycle Manager from your cluster operator-sdk olm uninstall ``` ### Non-OLM Installation - Testing hyperfleet-operator installation without OLM (kubectl apply) -1. **Create dist/install.yaml with override image:** +**Note:** Ensure `IMG` is properly exported before running these commands +1. **Quick testing on a k8s cluster:** ```bash - # Generate install.yaml with your custom image - make build-deployer-override-img - - # Alternative: manually edit config/manager/kustomization.yaml - # and run `make build-deployer` + export IMG="quay.io/$QUAY_USER/hyperfleet-operator:dev-" + make deploy # Generates: dist/install.yaml # Again, make sure to restore config/manager/kustomization.yaml after testing - ``` - -2. **Quick testing on a k8s cluster:** - ```bash - make deploy # Check status to see that everything installed properly + + # Cleanup - IMPORTANT: Delete CRs before uninstalling operator + # 1. Export and delete the cluster-scoped HyperFleetConfig CR + kubectl get hyperfleetconfig -o yaml > hyperfleetconfig-backup.yaml + kubectl delete hyperfleetconfig --all + + # 2. Undeploy operator (removes CRDs and controller) make undeploy + # Or manually: kubectl delete -f dist/install.yaml ``` - -**Note:** `bundle-override-img` and `build-deployer-override-img` modify config/manager/kustomization.yaml in place. So before committing any chnages make sure to revert these changes. Addiitonally when running `bundle-override-img` the bundle/ and bundle.Dockerfile get regenerated in place, so make sure to check these changes before committing them. - - - +**Note:** `bundle-override-img` and `build-deployer-override-img` modify config/manager/kustomization.yaml in place. So before committing any changes make sure to revert these changes. Additionally when running `bundle-override-img` the bundle/ and bundle.Dockerfile get regenerated in place, so make sure to check these changes before committing them.