From adcf6e9a329756944f0b12a8a907cc125f8ea2d3 Mon Sep 17 00:00:00 2001 From: Michael Peter Date: Thu, 12 Sep 2024 14:31:55 -0400 Subject: [PATCH 001/694] =?UTF-8?q?=F0=9F=93=96=20[Docs]=20Supported=20ext?= =?UTF-8?q?ensions=20(OLM=20v1=20constraints/limitations)=20(#1263)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #984 --- docs/Tasks/installing-an-extension.md | 4 ++-- docs/refs/supported-extensions.md | 18 ++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 docs/refs/supported-extensions.md diff --git a/docs/Tasks/installing-an-extension.md b/docs/Tasks/installing-an-extension.md index 81a687042a..1458a1653e 100644 --- a/docs/Tasks/installing-an-extension.md +++ b/docs/Tasks/installing-an-extension.md @@ -6,11 +6,11 @@ After you add a catalog to your cluster, you can install an extension by creatin ## Prerequisites * A deployed and unpacked catalog -* The name, and optionally version, or channel, of the extension to be installed -* The extension must be compatible with OLM 1.0 (see [current OLM v1 limitations](../drafts/refs/olmv1-limitations.md)) +* The name, and optionally version, or channel, of the [supported extension](../concepts/supported-extensions.md) to be installed * An existing namespace in which to install the extension * A suitable service account for installation (more information can be found [here](../drafts/Tasks/create-installer-service-account.md)) + ## Procedure 1. Create a CR for the Kubernetes extension you want to install: diff --git a/docs/refs/supported-extensions.md b/docs/refs/supported-extensions.md new file mode 100644 index 0000000000..8a1e97c029 --- /dev/null +++ b/docs/refs/supported-extensions.md @@ -0,0 +1,18 @@ +Currently, OLM v1 supports installing cluster extensions that meet the following criteria: + +* The extension must support installation via the `AllNamespaces` install mode. +* The extension must not use webhooks. +* The extension must not declare dependencies using the any of following file-based catalog properties: + + * `olm.gvk.required` + * `olm.package.required` + * `olm.constraint` + +When you install an extension, OLM v1 validates that the bundle you want to install meets these constraints. If you try to install an extension that does not meet these constraints, an error message is printed in the cluster extension's conditions. + +!!! important + + OLM v1 does not support the `OperatorConditions` API introduced in legacy OLM. + + Currently, there is no testing to validate against this constraint. If an extension uses the `OperatorConditions` API, the extension does not install correctly. Most extensions that rely on this API fail at start time, but some might fail during reconcilation. + diff --git a/mkdocs.yml b/mkdocs.yml index 69c3f015c0..e5a4d67177 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -17,6 +17,7 @@ nav: - Installing an extension: 'Tasks/installing-an-extension.md' - Deleting an extension: 'Tasks/uninstalling-an-extension.md' - References: + - Supported extensions: 'refs//supported-extensions.md' - API references: - Operator controller API reference: 'refs/api/operator-controller-api-reference.md' - Catalog queries: 'refs/catalog-queries.md' From 097d897611ec72e85e5c0d92723466846aa2d06c Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Thu, 12 Sep 2024 15:12:26 -0400 Subject: [PATCH 002/694] =?UTF-8?q?=E2=9C=A8unpacker:=20switch=20from=20go?= =?UTF-8?q?ogle/go-containerregistry=20to=20containers/image=20(#1194)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * unpacker: switch from google/go-containerregistry to containers/image Signed-off-by: Joe Lanford * use insecure registry to exercise /etc/containers in e2e Signed-off-by: Joe Lanford --------- Signed-off-by: Joe Lanford --- .goreleaser.yml | 4 +- Makefile | 21 +- Tiltfile | 2 +- cmd/manager/main.go | 13 +- .../registries-conf/kustomization.yaml | 7 + .../manager_e2e_registries_conf_patch.yaml | 18 + .../registries_conf_configmap.yaml | 11 + config/overlays/e2e/kustomization.yaml | 1 + .../olm_v1alpha1_clusterextension.yaml | 24 +- go.mod | 39 +- go.sum | 91 +++- hack/test/image-registry.sh | 21 +- .../clusterextension_controller.go | 2 +- internal/controllers/common_controller.go | 3 + internal/rukpak/source/containers_image.go | 276 +++++++++++++ .../rukpak/source/containers_image_test.go | 391 ++++++++++++++++++ internal/rukpak/source/image_registry.go | 197 --------- internal/rukpak/source/image_registry_test.go | 360 ---------------- internal/rukpak/source/unpacker.go | 46 +-- test/e2e/cluster_extension_install_test.go | 5 +- 20 files changed, 897 insertions(+), 635 deletions(-) create mode 100644 config/components/registries-conf/kustomization.yaml create mode 100644 config/components/registries-conf/manager_e2e_registries_conf_patch.yaml create mode 100644 config/components/registries-conf/registries_conf_configmap.yaml create mode 100644 internal/rukpak/source/containers_image.go create mode 100644 internal/rukpak/source/containers_image_test.go delete mode 100644 internal/rukpak/source/image_registry.go delete mode 100644 internal/rukpak/source/image_registry_test.go diff --git a/.goreleaser.yml b/.goreleaser.yml index 19358457a5..57e9dd8b6a 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -9,6 +9,8 @@ builds: asmflags: "{{ .Env.GO_BUILD_ASMFLAGS }}" gcflags: "{{ .Env.GO_BUILD_GCFLAGS }}" ldflags: "{{ .Env.GO_BUILD_LDFLAGS }}" + tags: + - "{{ .Env.GO_BUILD_TAGS }}" goos: - linux goarch: @@ -73,4 +75,4 @@ release: ```bash curl -L -s https://github.com/operator-framework/operator-controller/releases/download/{{ .Tag }}/install.sh | bash -s - ``` \ No newline at end of file + ``` diff --git a/Makefile b/Makefile index f36df3f915..f92abb7ca0 100644 --- a/Makefile +++ b/Makefile @@ -91,7 +91,7 @@ help-extended: #HELP Display extended help. .PHONY: lint lint: $(GOLANGCI_LINT) #HELP Run golangci linter. - $(GOLANGCI_LINT) run $(GOLANGCI_LINT_ARGS) + $(GOLANGCI_LINT) run --build-tags $(GO_BUILD_TAGS) $(GOLANGCI_LINT_ARGS) .PHONY: tidy tidy: #HELP Update dependencies. @@ -111,7 +111,7 @@ verify: tidy fmt vet generate manifests crd-ref-docs #HELP Verify all generated .PHONY: fix-lint fix-lint: $(GOLANGCI_LINT) #EXHELP Fix lint issues - $(GOLANGCI_LINT) run --fix $(GOLANGCI_LINT_ARGS) + $(GOLANGCI_LINT) run --fix --build-tags $(GO_BUILD_TAGS) $(GOLANGCI_LINT_ARGS) .PHONY: fmt fmt: #EXHELP Formats code @@ -119,7 +119,7 @@ fmt: #EXHELP Formats code .PHONY: vet vet: #EXHELP Run go vet against code. - go vet ./... + go vet -tags '$(GO_BUILD_TAGS)' ./... .PHONY: bingo-upgrade bingo-upgrade: $(BINGO) #EXHELP Upgrade tools @@ -155,10 +155,17 @@ UNIT_TEST_DIRS := $(shell go list ./... | grep -v /test/) COVERAGE_UNIT_DIR := $(ROOT_DIR)/coverage/unit test-unit: $(SETUP_ENVTEST) #HELP Run the unit tests rm -rf $(COVERAGE_UNIT_DIR) && mkdir -p $(COVERAGE_UNIT_DIR) - eval $$($(SETUP_ENVTEST) use -p env $(ENVTEST_VERSION) $(SETUP_ENVTEST_BIN_DIR_OVERRIDE)) && CGO_ENABLED=1 go test -count=1 -race -short $(UNIT_TEST_DIRS) -cover -coverprofile ${ROOT_DIR}/coverage/unit.out -test.gocoverdir=$(ROOT_DIR)/coverage/unit - + eval $$($(SETUP_ENVTEST) use -p env $(ENVTEST_VERSION) $(SETUP_ENVTEST_BIN_DIR_OVERRIDE)) && \ + CGO_ENABLED=1 go test \ + -tags '$(GO_BUILD_TAGS)' \ + -cover -coverprofile ${ROOT_DIR}/coverage/unit.out \ + -count=1 -race -short \ + $(UNIT_TEST_DIRS) \ + -test.gocoverdir=$(ROOT_DIR)/coverage/unit + +E2E_REGISTRY_CERT_REF := ClusterIssuer/olmv1-ca # By default, we'll use a trusted CA for the registry. image-registry: ## Setup in-cluster image registry - ./hack/test/image-registry.sh $(E2E_REGISTRY_NAMESPACE) $(E2E_REGISTRY_NAME) + ./hack/test/image-registry.sh $(E2E_REGISTRY_NAMESPACE) $(E2E_REGISTRY_NAME) $(E2E_REGISTRY_CERT_REF) build-push-e2e-catalog: ## Build the testdata catalog used for e2e tests and push it to the image registry ./hack/test/build-push-e2e-catalog.sh $(E2E_REGISTRY_NAMESPACE) $(LOCAL_REGISTRY_HOST)/$(E2E_TEST_CATALOG_V1) @@ -173,6 +180,7 @@ build-push-e2e-catalog: ## Build the testdata catalog used for e2e tests and pus test-e2e: KIND_CLUSTER_NAME := operator-controller-e2e test-e2e: KUSTOMIZE_BUILD_DIR := config/overlays/e2e test-e2e: GO_BUILD_FLAGS := -cover +test-e2e: E2E_REGISTRY_CERT_REF := Issuer/selfsigned-issuer test-e2e: run image-registry build-push-e2e-catalog registry-load-bundles e2e e2e-coverage kind-clean #HELP Run e2e test suite on local kind cluster .PHONY: extension-developer-e2e @@ -243,6 +251,7 @@ export CGO_ENABLED export GIT_REPO := $(shell go list -m) export VERSION_PATH := ${GIT_REPO}/internal/version +export GO_BUILD_TAGS := containers_image_openpgp export GO_BUILD_ASMFLAGS := all=-trimpath=$(PWD) export GO_BUILD_GCFLAGS := all=-trimpath=$(PWD) export GO_BUILD_FLAGS := diff --git a/Tiltfile b/Tiltfile index 3302129023..10c4362e14 100644 --- a/Tiltfile +++ b/Tiltfile @@ -18,6 +18,6 @@ repo = { for r in repos: if r == 'operator-controller': - deploy_repo('operator-controller', repo) + deploy_repo('operator-controller', repo, '-tags containers_image_openpgp') else: include('../{}/Tiltfile'.format(r)) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 95813cf6c1..db25c3ad07 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -25,6 +25,7 @@ import ( "path/filepath" "time" + "github.com/containers/image/v5/types" "github.com/spf13/pflag" "go.uber.org/zap/zapcore" apiextensionsv1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" @@ -194,14 +195,18 @@ func main() { setupLog.Error(err, "unable to create CA certificate pool") os.Exit(1) } - unpacker := &source.ImageRegistry{ - BaseCachePath: filepath.Join(cachePath, "unpack"), - CertPoolWatcher: certPoolWatcher, + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: filepath.Join(cachePath, "unpack"), + SourceContext: &types.SystemContext{ + DockerCertPath: caCertDir, + OCICertPath: caCertDir, + }, } clusterExtensionFinalizers := crfinalizer.NewFinalizers() if err := clusterExtensionFinalizers.Register(controllers.ClusterExtensionCleanupUnpackCacheFinalizer, finalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { - return crfinalizer.Result{}, os.RemoveAll(filepath.Join(unpacker.BaseCachePath, obj.GetName())) + return crfinalizer.Result{}, unpacker.Cleanup(ctx, &source.BundleSource{Name: obj.GetName()}) })); err != nil { setupLog.Error(err, "unable to register finalizer", "finalizerKey", controllers.ClusterExtensionCleanupUnpackCacheFinalizer) os.Exit(1) diff --git a/config/components/registries-conf/kustomization.yaml b/config/components/registries-conf/kustomization.yaml new file mode 100644 index 0000000000..e482624290 --- /dev/null +++ b/config/components/registries-conf/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component +namespace: olmv1-system +resources: +- registries_conf_configmap.yaml +patches: +- path: manager_e2e_registries_conf_patch.yaml diff --git a/config/components/registries-conf/manager_e2e_registries_conf_patch.yaml b/config/components/registries-conf/manager_e2e_registries_conf_patch.yaml new file mode 100644 index 0000000000..7530f9b082 --- /dev/null +++ b/config/components/registries-conf/manager_e2e_registries_conf_patch.yaml @@ -0,0 +1,18 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: kube-rbac-proxy + - name: manager + volumeMounts: + - name: e2e-registries-conf + mountPath: /etc/containers + volumes: + - name: e2e-registries-conf + configMap: + name: e2e-registries-conf diff --git a/config/components/registries-conf/registries_conf_configmap.yaml b/config/components/registries-conf/registries_conf_configmap.yaml new file mode 100644 index 0000000000..df33edb411 --- /dev/null +++ b/config/components/registries-conf/registries_conf_configmap.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: e2e-registries-conf + namespace: system +data: + registries.conf: | + [[registry]] + prefix = "docker-registry.operator-controller-e2e.svc.cluster.local:5000" + insecure = true + location = "docker-registry.operator-controller-e2e.svc.cluster.local:5000" diff --git a/config/overlays/e2e/kustomization.yaml b/config/overlays/e2e/kustomization.yaml index 626ecb6196..4a40576bd8 100644 --- a/config/overlays/e2e/kustomization.yaml +++ b/config/overlays/e2e/kustomization.yaml @@ -7,5 +7,6 @@ resources: components: - ../../components/tls - ../../components/coverage +- ../../components/registries-conf # ca must be last or (tls|coverage) will overwrite the namespaces - ../../components/ca diff --git a/config/samples/olm_v1alpha1_clusterextension.yaml b/config/samples/olm_v1alpha1_clusterextension.yaml index 19bbf06b0e..7536c3d90e 100644 --- a/config/samples/olm_v1alpha1_clusterextension.yaml +++ b/config/samples/olm_v1alpha1_clusterextension.yaml @@ -49,20 +49,20 @@ rules: # Manage ArgoCD ClusterRoles and ClusterRoleBindings - apiGroups: [rbac.authorization.k8s.io] resources: [clusterroles] - verbs: [create] + verbs: [create, list, watch] - apiGroups: [rbac.authorization.k8s.io] resources: [clusterroles] - verbs: [get, list, watch, update, patch, delete] + verbs: [get, update, patch, delete] resourceNames: - argocd-operator.v0-1dhiybrldl1gyksid1dk2dqjsc72psdybc7iyvse5gpx - argocd-operator-metrics-reader - argocd-operator.v0-22gmilmgp91wu25is5i2ec598hni8owq3l71bbkl7iz3 - apiGroups: [rbac.authorization.k8s.io] resources: [clusterrolebindings] - verbs: [create] + verbs: [create, list, watch] - apiGroups: [rbac.authorization.k8s.io] resources: [clusterrolebindings] - verbs: [get, list, watch, update, patch, delete] + verbs: [get, update, patch, delete] resourceNames: - argocd-operator.v0-1dhiybrldl1gyksid1dk2dqjsc72psdybc7iyvse5gpx - argocd-operator.v0-22gmilmgp91wu25is5i2ec598hni8owq3l71bbkl7iz3 @@ -226,31 +226,31 @@ metadata: rules: - apiGroups: [""] resources: [serviceaccounts] - verbs: [create] + verbs: [create, list, watch] - apiGroups: [""] resources: [serviceaccounts] - verbs: [get, list, watch, update, patch, delete] + verbs: [get, update, patch, delete] resourceNames: [argocd-operator-controller-manager] - apiGroups: [""] resources: [configmaps] - verbs: [create] + verbs: [create, list, watch] - apiGroups: [""] resources: [configmaps] - verbs: [get, list, watch, update, patch, delete] + verbs: [get, update, patch, delete] resourceNames: [argocd-operator-manager-config] - apiGroups: [""] resources: [services] - verbs: [create] + verbs: [create, list, watch] - apiGroups: [""] resources: [services] - verbs: [get, list, watch, update, patch, delete] + verbs: [get, update, patch, delete] resourceNames: [argocd-operator-controller-manager-metrics-service] - apiGroups: [apps] resources: [deployments] - verbs: [create] + verbs: [create, list, watch] - apiGroups: [apps] resources: [deployments] - verbs: [get, list, watch, update, patch, delete] + verbs: [get, update, patch, delete] resourceNames: [argocd-operator-controller-manager] --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/go.mod b/go.mod index e71bc123c9..0829548c3c 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,19 @@ go 1.22.5 require ( carvel.dev/kapp v0.63.3 + github.com/BurntSushi/toml v1.4.0 github.com/Masterminds/semver/v3 v3.3.0 github.com/blang/semver/v4 v4.0.0 github.com/containerd/containerd v1.7.22 + github.com/containers/image/v5 v5.32.2 github.com/fsnotify/fsnotify v1.7.0 github.com/go-logr/logr v1.4.2 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.2 + github.com/olareg/olareg v0.1.1 github.com/onsi/ginkgo/v2 v2.20.2 github.com/onsi/gomega v1.34.2 + github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 github.com/operator-framework/catalogd v0.26.0 github.com/operator-framework/helm-operator-plugins v0.5.0 @@ -38,13 +42,14 @@ require ( carvel.dev/vendir v0.40.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect - github.com/BurntSushi/toml v1.4.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.5 // indirect + github.com/VividCortex/ewma v1.2.0 // indirect + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -61,13 +66,13 @@ require ( github.com/containerd/ttrpc v1.2.5 // indirect github.com/containerd/typeurl/v2 v2.1.1 // indirect github.com/containers/common v0.60.2 // indirect - github.com/containers/image/v5 v5.32.2 // indirect github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect github.com/containers/ocicrypt v1.2.0 // indirect github.com/containers/storage v1.55.0 // indirect github.com/cppforlife/cobrautil v0.0.0-20221130162803-acdfead391ef // indirect github.com/cppforlife/color v1.9.1-0.20200716202919-6706ac40b835 // indirect github.com/cppforlife/go-cli-ui v0.0.0-20220425131040-94f26b16bc14 // indirect + github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f // indirect github.com/cyphar/filepath-securejoin v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect @@ -90,11 +95,19 @@ require ( github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-git/go-git/v5 v5.12.0 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/analysis v0.23.0 // indirect + github.com/go-openapi/errors v0.22.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/loads v0.22.0 // indirect + github.com/go-openapi/runtime v0.28.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/validate v0.24.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -128,17 +141,22 @@ require ( github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 // indirect github.com/k14s/ytt v0.36.0 // indirect github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/letsencrypt/boulder v0.0.0-20240418210053-89b07f4543e0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-sqlite3 v1.14.23 // indirect + github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.4.0 // indirect @@ -152,7 +170,7 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 // indirect @@ -161,6 +179,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/proglottis/gpgme v0.1.3 // indirect github.com/prometheus/client_golang v1.20.3 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect @@ -168,14 +187,22 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/rubenv/sql-migrate v1.5.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect + github.com/sigstore/fulcio v1.4.5 // indirect + github.com/sigstore/rekor v1.3.6 // indirect + github.com/sigstore/sigstore v1.8.4 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cobra v1.8.1 // indirect + github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect + github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect + github.com/ulikunitz/xz v0.5.12 // indirect github.com/vbatts/tar-split v0.11.5 // indirect + github.com/vbauerster/mpb/v8 v8.7.5 // indirect github.com/vito/go-interact v1.0.1 // indirect github.com/vmware-tanzu/carvel-kapp-controller v0.51.0 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -184,6 +211,8 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect go.etcd.io/bbolt v1.3.11 // indirect + go.mongodb.org/mongo-driver v1.14.0 // indirect + go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect go.opentelemetry.io/otel v1.28.0 // indirect @@ -205,7 +234,7 @@ require ( golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.66.0 // indirect diff --git a/go.sum b/go.sum index ae892a5162..17275b18cc 100644 --- a/go.sum +++ b/go.sum @@ -14,7 +14,11 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -41,6 +45,10 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/Microsoft/hcsshim v0.12.5 h1:bpTInLlDy/nDRWFVcefDZZ1+U8tS+rz3MxjKgu9boo0= github.com/Microsoft/hcsshim v0.12.5/go.mod h1:tIUGego4G1EN5Hb6KC90aDYiUI2dqLSTTOCjVNpOgZ8= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= +github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= @@ -132,6 +140,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f h1:eHnXnuK47UlSTOQexbzxAZfekVz6i+LKRdj1CU5DPaM= +github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/cyphar/filepath-securejoin v0.3.1 h1:1V7cHiaW+C+39wEfpH6XlLBQo3j/PciWFrgfCLS8XrE= github.com/cyphar/filepath-securejoin v0.3.1/go.mod h1:F7i41x/9cBF7lzCrVsYs9fuzwRZm4NQsGTBdpp6mETc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -183,8 +193,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -203,6 +213,8 @@ github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXY github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= +github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -213,17 +225,34 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= +github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= +github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= +github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= +github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= +github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= +github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= +github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= +github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= +github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= @@ -313,8 +342,9 @@ github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99 h1:JYghRBlGCZyCF2wNUJ8W0cwaQdtpcssJ4CgC406g+WU= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= @@ -344,7 +374,7 @@ github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw= github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU= github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= @@ -367,6 +397,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= +github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joelanford/ignore v0.1.0 h1:VawbTDeg5EL+PN7W8gxVzGerfGpVo3gFdR5ZAqnkYRk= @@ -414,6 +446,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/letsencrypt/boulder v0.0.0-20240418210053-89b07f4543e0 h1:aiPrFdHDCCvigNBCkOWj2lv9Bx5xDp210OANZEoiP0I= +github.com/letsencrypt/boulder v0.0.0-20240418210053-89b07f4543e0/go.mod h1:srVwm2N3DC/tWqQ+igZXDrmKlNRN8X/dmJ1wEZrv760= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -445,8 +479,10 @@ github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWt github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg= -github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= +github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= +github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -461,6 +497,8 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -493,9 +531,12 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olareg/olareg v0.1.1 h1:Ui7q93zjcoF+U9U71sgqgZWByDoZOpqHitUXEu2xV+g= +github.com/olareg/olareg v0.1.1/go.mod h1:w8NP4SWrHHtxsFaUiv1lnCnYPm4sN1seCd2h7FK/dc0= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= @@ -546,6 +587,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= +github.com/proglottis/gpgme v0.1.3 h1:Crxx0oz4LKB3QXc5Ea0J19K/3ICfy3ftr5exgUK1AU0= +github.com/proglottis/gpgme v0.1.3/go.mod h1:fPbW/EZ0LvwQtH8Hy7eixhp1eF3G39dtx7GUN+0Gmy0= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -574,8 +617,8 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fO github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= -github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= -github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= +github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= +github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -591,12 +634,20 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA= +github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sigstore/fulcio v1.4.5 h1:WWNnrOknD0DbruuZWCbN+86WRROpEl3Xts+WT2Ek1yc= +github.com/sigstore/fulcio v1.4.5/go.mod h1:oz3Qwlma8dWcSS/IENR/6SjbW4ipN0cxpRVfgdsjMU8= +github.com/sigstore/rekor v1.3.6 h1:QvpMMJVWAp69a3CHzdrLelqEqpTM3ByQRt5B5Kspbi8= +github.com/sigstore/rekor v1.3.6/go.mod h1:JDTSNNMdQ/PxdsS49DJkJ+pRJCO/83nbR5p3aZQteXc= +github.com/sigstore/sigstore v1.8.4 h1:g4ICNpiENFnWxjmBzBDWUn62rNFeny/P77HUC8da32w= +github.com/sigstore/sigstore v1.8.4/go.mod h1:1jIKtkTFEeISen7en+ZPWdDHazqhxco/+v9CNjc7oNg= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -607,8 +658,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -617,6 +668,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw= +github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -638,12 +691,16 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= +github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= +github.com/vbauerster/mpb/v8 v8.7.5 h1:hUF3zaNsuaBBwzEFoCvfuX3cpesQXZC0Phm/JcHZQ+c= +github.com/vbauerster/mpb/v8 v8.7.5/go.mod h1:bRCnR7K+mj5WXKsy0NWB6Or+wctYGvVwKn6huwvxKa0= github.com/vito/go-interact v0.0.0-20171111012221-fa338ed9e9ec/go.mod h1:wPlfmglZmRWMYv/qJy3P+fK/UnoQB5ISk4txfNd9tDo= github.com/vito/go-interact v1.0.1 h1:O8xi8c93bRUv2Tb/v6HdiuGc+WnWt+AQzF74MOOdlBs= github.com/vito/go-interact v1.0.1/go.mod h1:HrdHSJXD2yn1MhlTwSIMeFgQ5WftiIorszVGd3S/DAA= @@ -673,6 +730,10 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxv go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI= go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= +go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= +go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= @@ -903,8 +964,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 h1:ImUcDPHjTrAqNhlOkSocDLfG9rrNHH7w7uoKWPaWZ8s= +google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U= google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= diff --git a/hack/test/image-registry.sh b/hack/test/image-registry.sh index bbfa096a82..1187de77d8 100755 --- a/hack/test/image-registry.sh +++ b/hack/test/image-registry.sh @@ -7,14 +7,16 @@ set -o pipefail help=" image-registry.sh is a script to stand up an image registry within a cluster. Usage: - image-registry.sh [NAMESPACE] [NAME] + image-registry.sh [NAMESPACE] [NAME] [CERT_REF] Argument Descriptions: - NAMESPACE is the namespace that should be created and is the namespace in which the image registry will be created - NAME is the name that should be used for the image registry Deployment and Service + - CERT_REF is the reference to the CA certificate that should be used to serve the image registry over HTTPS, in the + format of 'Issuer/' or 'ClusterIssuer/' " -if [[ "$#" -ne 2 ]]; then +if [[ "$#" -ne 3 ]]; then echo "Illegal number of arguments passed" echo "${help}" exit 1 @@ -22,12 +24,23 @@ fi namespace=$1 name=$2 +certRef=$3 + +echo "CERT_REF: ${certRef}" kubectl apply -f - << EOF apiVersion: v1 kind: Namespace metadata: name: ${namespace} +--- + apiVersion: cert-manager.io/v1 + kind: Issuer + metadata: + name: selfsigned-issuer + namespace: ${namespace} + spec: + selfSigned: {} --- apiVersion: cert-manager.io/v1 kind: Certificate @@ -44,8 +57,8 @@ spec: algorithm: ECDSA size: 256 issuerRef: - name: olmv1-ca - kind: ClusterIssuer + name: ${certRef#*/} + kind: ${certRef%/*} group: cert-manager.io --- apiVersion: apps/v1 diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index c2830dc291..dc3dd72475 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -266,7 +266,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, "unpack pending") return ctrl.Result{}, nil case rukpaksource.StateUnpacked: - setStatusUnpacked(ext, fmt.Sprintf("unpack successful: %v", unpackResult.Message)) + setStatusUnpacked(ext, unpackResult.Message) default: setStatusUnpackFailed(ext, "unexpected unpack status") // We previously exit with a failed status if error is not nil. diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index 6e94bcc10c..8beeb3e31f 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -79,6 +79,9 @@ func setStatusUnpackFailed(ext *ocv1alpha1.ClusterExtension, message string) { } func setStatusUnpacked(ext *ocv1alpha1.ClusterExtension, message string) { + if message == "" { + message = "unpack successful" + } apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ Type: ocv1alpha1.TypeUnpacked, Status: metav1.ConditionTrue, diff --git a/internal/rukpak/source/containers_image.go b/internal/rukpak/source/containers_image.go new file mode 100644 index 0000000000..60841ea498 --- /dev/null +++ b/internal/rukpak/source/containers_image.go @@ -0,0 +1,276 @@ +package source + +import ( + "archive/tar" + "context" + "errors" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/containerd/containerd/archive" + "github.com/containers/image/v5/copy" + "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/oci/layout" + "github.com/containers/image/v5/pkg/blobinfocache/none" + "github.com/containers/image/v5/pkg/compression" + "github.com/containers/image/v5/signature" + "github.com/containers/image/v5/types" + "github.com/opencontainers/go-digest" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +type ContainersImageRegistry struct { + BaseCachePath string + SourceContext *types.SystemContext +} + +func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSource) (*Result, error) { + l := log.FromContext(ctx) + + if bundle.Type != SourceTypeImage { + panic(fmt.Sprintf("programmer error: source type %q is unable to handle specified bundle source type %q", SourceTypeImage, bundle.Type)) + } + + if bundle.Image == nil { + return nil, NewUnrecoverable(fmt.Errorf("error parsing bundle, bundle %s has a nil image source", bundle.Name)) + } + + ////////////////////////////////////////////////////// + // + // Resolve a canonical reference for the image. + // + ////////////////////////////////////////////////////// + imgRef, err := reference.ParseNamed(bundle.Image.Ref) + if err != nil { + return nil, NewUnrecoverable(fmt.Errorf("error parsing image reference %q: %w", bundle.Image.Ref, err)) + } + + canonicalRef, err := resolveCanonicalRef(ctx, imgRef, i.SourceContext) + if err != nil { + return nil, fmt.Errorf("error resolving canonical reference: %w", err) + } + + ////////////////////////////////////////////////////// + // + // Check if the image is already unpacked. If it is, + // return the unpacked directory. + // + ////////////////////////////////////////////////////// + unpackPath := i.unpackPath(bundle.Name, canonicalRef.Digest()) + if unpackStat, err := os.Stat(unpackPath); err == nil { + if !unpackStat.IsDir() { + return nil, fmt.Errorf("unexpected file at unpack path %q: expected a directory", unpackPath) + } + l.Info("image already unpacked", "ref", imgRef.String(), "digest", canonicalRef.Digest().String()) + return successResult(bundle.Name, unpackPath, canonicalRef), nil + } + + ////////////////////////////////////////////////////// + // + // Create a docker reference for the source and an OCI + // layout reference for the destination, where we will + // temporarily store the image in order to unpack it. + // + // We use the OCI layout as a temporary storage because + // copy.Image can concurrently pull all the layers. + // + ////////////////////////////////////////////////////// + dockerRef, err := docker.NewReference(canonicalRef) + if err != nil { + return nil, fmt.Errorf("error creating source reference: %w", err) + } + + layoutDir, err := os.MkdirTemp("", fmt.Sprintf("oci-layout-%s", bundle.Name)) + if err != nil { + return nil, fmt.Errorf("error creating temporary directory: %w", err) + } + defer os.RemoveAll(layoutDir) + + layoutRef, err := layout.NewReference(layoutDir, canonicalRef.String()) + if err != nil { + return nil, fmt.Errorf("error creating reference: %w", err) + } + + ////////////////////////////////////////////////////// + // + // Load an image signature policy and build + // a policy context for the image pull. + // + ////////////////////////////////////////////////////// + policy, err := signature.DefaultPolicy(i.SourceContext) + if os.IsNotExist(err) { + l.Info("no default policy found, using insecure policy") + policy, err = signature.NewPolicyFromBytes([]byte(`{"default":[{"type":"insecureAcceptAnything"}]}`)) + } + if err != nil { + return nil, fmt.Errorf("error getting policy: %w", err) + } + policyContext, err := signature.NewPolicyContext(policy) + if err != nil { + return nil, fmt.Errorf("error getting policy context: %w", err) + } + defer func() { + if err := policyContext.Destroy(); err != nil { + l.Error(err, "error destroying policy context") + } + }() + + ////////////////////////////////////////////////////// + // + // Pull the image from the source to the destination + // + ////////////////////////////////////////////////////// + if _, err := copy.Image(ctx, policyContext, layoutRef, dockerRef, ©.Options{ + SourceCtx: i.SourceContext, + }); err != nil { + return nil, fmt.Errorf("error copying image: %w", err) + } + l.Info("pulled image", "ref", imgRef.String(), "digest", canonicalRef.Digest().String()) + + ////////////////////////////////////////////////////// + // + // Mount the image we just pulled + // + ////////////////////////////////////////////////////// + if err := i.unpackImage(ctx, unpackPath, layoutRef); err != nil { + return nil, fmt.Errorf("error unpacking image: %w", err) + } + + ////////////////////////////////////////////////////// + // + // Delete other images. They are no longer needed. + // + ////////////////////////////////////////////////////// + if err := i.deleteOtherImages(bundle.Name, canonicalRef.Digest()); err != nil { + return nil, fmt.Errorf("error deleting old images: %w", err) + } + + return successResult(bundle.Name, unpackPath, canonicalRef), nil +} + +func successResult(bundleName, unpackPath string, canonicalRef reference.Canonical) *Result { + return &Result{ + Bundle: os.DirFS(unpackPath), + ResolvedSource: &BundleSource{Type: SourceTypeImage, Name: bundleName, Image: &ImageSource{Ref: canonicalRef.String()}}, + State: StateUnpacked, + Message: fmt.Sprintf("unpacked %q successfully", canonicalRef), + } +} + +func (i *ContainersImageRegistry) Cleanup(_ context.Context, bundle *BundleSource) error { + return os.RemoveAll(i.bundlePath(bundle.Name)) +} + +func (i *ContainersImageRegistry) bundlePath(bundleName string) string { + return filepath.Join(i.BaseCachePath, bundleName) +} + +func (i *ContainersImageRegistry) unpackPath(bundleName string, digest digest.Digest) string { + return filepath.Join(i.bundlePath(bundleName), digest.String()) +} + +func resolveCanonicalRef(ctx context.Context, imgRef reference.Named, imageCtx *types.SystemContext) (reference.Canonical, error) { + if canonicalRef, ok := imgRef.(reference.Canonical); ok { + return canonicalRef, nil + } + + srcRef, err := docker.NewReference(imgRef) + if err != nil { + return nil, NewUnrecoverable(fmt.Errorf("error creating reference: %w", err)) + } + + imgSrc, err := srcRef.NewImageSource(ctx, imageCtx) + if err != nil { + return nil, fmt.Errorf("error creating image source: %w", err) + } + defer imgSrc.Close() + + imgManifestData, _, err := imgSrc.GetManifest(ctx, nil) + if err != nil { + return nil, fmt.Errorf("error getting manifest: %w", err) + } + imgDigest, err := manifest.Digest(imgManifestData) + if err != nil { + return nil, fmt.Errorf("error getting digest of manifest: %w", err) + } + return reference.WithDigest(reference.TrimNamed(imgRef), imgDigest) +} + +func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath string, imageReference types.ImageReference) error { + img, err := imageReference.NewImage(ctx, i.SourceContext) + if err != nil { + return fmt.Errorf("error reading image: %w", err) + } + defer func() { + if err := img.Close(); err != nil { + panic(err) + } + }() + + layoutSrc, err := imageReference.NewImageSource(ctx, i.SourceContext) + if err != nil { + return fmt.Errorf("error creating image source: %w", err) + } + + if err := os.MkdirAll(unpackPath, 0755); err != nil { + return fmt.Errorf("error creating unpack directory: %w", err) + } + l := log.FromContext(ctx) + l.Info("unpacking image", "path", unpackPath) + for i, layerInfo := range img.LayerInfos() { + if err := func() error { + layerReader, _, err := layoutSrc.GetBlob(ctx, layerInfo, none.NoCache) + if err != nil { + return fmt.Errorf("error getting blob for layer[%d]: %w", i, err) + } + defer layerReader.Close() + + if err := applyLayer(ctx, unpackPath, layerReader); err != nil { + return fmt.Errorf("error applying layer[%d]: %w", i, err) + } + l.Info("applied layer", "layer", i) + return nil + }(); err != nil { + return errors.Join(err, os.RemoveAll(unpackPath)) + } + } + return nil +} + +func applyLayer(ctx context.Context, unpackPath string, layer io.ReadCloser) error { + decompressed, _, err := compression.AutoDecompress(layer) + if err != nil { + return fmt.Errorf("auto-decompress failed: %w", err) + } + defer decompressed.Close() + + _, err = archive.Apply(ctx, unpackPath, decompressed, archive.WithFilter(func(h *tar.Header) (bool, error) { + h.Uid = os.Getuid() + h.Gid = os.Getgid() + h.Mode |= 0770 + return true, nil + })) + return err +} + +func (i *ContainersImageRegistry) deleteOtherImages(bundleName string, digestToKeep digest.Digest) error { + bundlePath := i.bundlePath(bundleName) + imgDirs, err := os.ReadDir(bundlePath) + if err != nil { + return fmt.Errorf("error reading image directories: %w", err) + } + for _, imgDir := range imgDirs { + if imgDir.Name() == digestToKeep.String() { + continue + } + imgDirPath := filepath.Join(bundlePath, imgDir.Name()) + if err := os.RemoveAll(imgDirPath); err != nil { + return fmt.Errorf("error removing image directory: %w", err) + } + } + return nil +} diff --git a/internal/rukpak/source/containers_image_test.go b/internal/rukpak/source/containers_image_test.go new file mode 100644 index 0000000000..1d1c42dcde --- /dev/null +++ b/internal/rukpak/source/containers_image_test.go @@ -0,0 +1,391 @@ +package source_test + +import ( + "context" + "fmt" + "io/fs" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "testing" + + "github.com/BurntSushi/toml" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/pkg/sysregistriesv2" + "github.com/containers/image/v5/types" + "github.com/google/go-containerregistry/pkg/crane" + "github.com/olareg/olareg" + "github.com/olareg/olareg/config" + "github.com/opencontainers/go-digest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/operator-framework/operator-controller/internal/rukpak/source" +) + +const ( + testFileName string = "test-file" + testFileContents string = "test-content" +) + +func TestUnpackValidInsecure(t *testing.T) { + imageTagRef, _, cleanup := setupRegistry(t) + defer cleanup() + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + SourceContext: buildPullContext(t, imageTagRef), + } + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + Ref: imageTagRef.String(), + }, + } + + oldBundlePath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, "old") + err := os.MkdirAll(oldBundlePath, 0755) + require.NoError(t, err) + + // Attempt to pull and unpack the image + result, err := unpacker.Unpack(context.Background(), bundleSource) + require.NoError(t, err) + require.NotNil(t, result) + assert.Equal(t, result.State, source.StateUnpacked) + + require.NoDirExists(t, oldBundlePath) + + unpackedFile, err := fs.ReadFile(result.Bundle, testFileName) + assert.NoError(t, err) + // Ensure the unpacked file matches the source content + assert.Equal(t, []byte(testFileContents), unpackedFile) +} + +func TestUnpackValidUsesCache(t *testing.T) { + _, imageDigestRef, cleanup := setupRegistry(t) + defer cleanup() + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + SourceContext: buildPullContext(t, imageDigestRef), + } + + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + Ref: imageDigestRef.String(), + }, + } + + // Populate the bundle cache with a folder that is not actually part of the image + testCacheFilePath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, imageDigestRef.Digest().String(), "test-folder") + require.NoError(t, os.MkdirAll(testCacheFilePath, 0700)) + + // Attempt to pull and unpack the image + result, err := unpacker.Unpack(context.Background(), bundleSource) + assert.NoError(t, err) + require.NotNil(t, result) + assert.Equal(t, result.State, source.StateUnpacked) + + // Make sure the original contents of the cache are still present. If the cached contents + // were not used, we would expect the original contents to be removed. + assert.DirExists(t, testCacheFilePath) +} + +func TestUnpackCacheCheckError(t *testing.T) { + imageTagRef, imageDigestRef, cleanup := setupRegistry(t) + defer cleanup() + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + SourceContext: buildPullContext(t, imageTagRef), + } + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + Ref: imageTagRef.String(), + }, + } + + // Create the unpack path and restrict its permissions + unpackPath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, imageDigestRef.Digest().String()) + require.NoError(t, os.MkdirAll(unpackPath, os.ModePerm)) + require.NoError(t, os.Chmod(unpacker.BaseCachePath, 0000)) + defer func() { + require.NoError(t, os.Chmod(unpacker.BaseCachePath, 0755)) + }() + + // Attempt to pull and unpack the image + _, err := unpacker.Unpack(context.Background(), bundleSource) + assert.ErrorContains(t, err, "permission denied") +} + +func TestUnpackNameOnlyImageReference(t *testing.T) { + imageTagRef, _, cleanup := setupRegistry(t) + defer cleanup() + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + SourceContext: buildPullContext(t, imageTagRef), + } + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + Ref: reference.TrimNamed(imageTagRef).String(), + }, + } + + // Attempt to pull and unpack the image + _, err := unpacker.Unpack(context.Background(), bundleSource) + assert.ErrorContains(t, err, "tag or digest is needed") + assert.ErrorAs(t, err, &source.Unrecoverable{}) +} + +func TestUnpackUnservedTaggedImageReference(t *testing.T) { + imageTagRef, _, cleanup := setupRegistry(t) + defer cleanup() + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + SourceContext: buildPullContext(t, imageTagRef), + } + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + // Use a valid reference that is not served + Ref: fmt.Sprintf("%s:unserved-tag", reference.TrimNamed(imageTagRef)), + }, + } + + // Attempt to pull and unpack the image + _, err := unpacker.Unpack(context.Background(), bundleSource) + assert.ErrorContains(t, err, "manifest unknown") +} + +func TestUnpackUnservedCanonicalImageReference(t *testing.T) { + imageTagRef, imageDigestRef, cleanup := setupRegistry(t) + defer cleanup() + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + SourceContext: buildPullContext(t, imageTagRef), + } + + origRef := imageDigestRef.String() + nonExistentRef := origRef[:len(origRef)-1] + "1" + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + // Use a valid reference that is not served + Ref: nonExistentRef, + }, + } + + // Attempt to pull and unpack the image + _, err := unpacker.Unpack(context.Background(), bundleSource) + assert.ErrorContains(t, err, "manifest unknown") +} + +func TestUnpackInvalidSourceType(t *testing.T) { + unpacker := &source.ContainersImageRegistry{} + // Create BundleSource with invalid source type + bundleSource := &source.BundleSource{ + Type: "invalid", + } + + shouldPanic := func() { + // Attempt to pull and unpack the image + _, err := unpacker.Unpack(context.Background(), bundleSource) + if err != nil { + t.Error("func should have panicked") + } + } + assert.Panics(t, shouldPanic) +} + +func TestUnpackInvalidNilImage(t *testing.T) { + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + } + // Create BundleSource with nil Image + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: nil, + } + + // Attempt to unpack + result, err := unpacker.Unpack(context.Background(), bundleSource) + assert.Nil(t, result) + assert.ErrorContains(t, err, "nil image source") + assert.ErrorAs(t, err, &source.Unrecoverable{}) + assert.NoDirExists(t, filepath.Join(unpacker.BaseCachePath, bundleSource.Name)) +} + +func TestUnpackInvalidImageRef(t *testing.T) { + unpacker := &source.ContainersImageRegistry{} + // Create BundleSource with malformed image reference + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + Ref: "invalid image ref", + }, + } + + // Attempt to unpack + result, err := unpacker.Unpack(context.Background(), bundleSource) + assert.Nil(t, result) + assert.ErrorContains(t, err, "error parsing image reference") + assert.ErrorAs(t, err, &source.Unrecoverable{}) + assert.NoDirExists(t, filepath.Join(unpacker.BaseCachePath, bundleSource.Name)) +} + +func TestUnpackUnexpectedFile(t *testing.T) { + imageTagRef, imageDigestRef, cleanup := setupRegistry(t) + defer cleanup() + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + SourceContext: buildPullContext(t, imageTagRef), + } + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + Ref: imageTagRef.String(), + }, + } + + // Create an unpack path that is a file + unpackPath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, imageDigestRef.Digest().String()) + require.NoError(t, os.MkdirAll(filepath.Dir(unpackPath), 0700)) + require.NoError(t, os.WriteFile(unpackPath, []byte{}, 0600)) + + // Attempt to pull and unpack the image + _, err := unpacker.Unpack(context.Background(), bundleSource) + assert.ErrorContains(t, err, "expected a directory") +} + +func TestUnpackCopySucceedsMountFails(t *testing.T) { + imageTagRef, _, cleanup := setupRegistry(t) + defer cleanup() + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + SourceContext: buildPullContext(t, imageTagRef), + } + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + Ref: imageTagRef.String(), + }, + } + + // Create an unpack path that is a non-writable directory + bundleDir := filepath.Join(unpacker.BaseCachePath, bundleSource.Name) + require.NoError(t, os.MkdirAll(bundleDir, 0000)) + + // Attempt to pull and unpack the image + _, err := unpacker.Unpack(context.Background(), bundleSource) + assert.ErrorContains(t, err, "permission denied") +} + +func TestCleanup(t *testing.T) { + imageTagRef, _, cleanup := setupRegistry(t) + defer cleanup() + + unpacker := &source.ContainersImageRegistry{ + BaseCachePath: t.TempDir(), + SourceContext: buildPullContext(t, imageTagRef), + } + bundleSource := &source.BundleSource{ + Name: "test-bundle", + Type: source.SourceTypeImage, + Image: &source.ImageSource{ + Ref: imageTagRef.String(), + }, + } + + // Create an unpack path for the bundle + bundleDir := filepath.Join(unpacker.BaseCachePath, bundleSource.Name) + require.NoError(t, os.MkdirAll(bundleDir, 0755)) + + // Clean up the bundle + err := unpacker.Cleanup(context.Background(), bundleSource) + assert.NoError(t, err) + assert.NoDirExists(t, bundleDir) +} + +func setupRegistry(t *testing.T) (reference.NamedTagged, reference.Canonical, func()) { + regHandler := olareg.New(config.Config{ + Storage: config.ConfigStorage{ + StoreType: config.StoreMem, + }, + }) + server := httptest.NewServer(regHandler) + serverURL, err := url.Parse(server.URL) + require.NoError(t, err) + + // Generate an image with file contents + img, err := crane.Image(map[string][]byte{testFileName: []byte(testFileContents)}) + require.NoError(t, err) + + imageTagRef, err := newReference(serverURL.Host, "test-repo/test-image", "test-tag") + require.NoError(t, err) + + imgDigest, err := img.Digest() + require.NoError(t, err) + + imageDigestRef, err := reference.WithDigest(reference.TrimNamed(imageTagRef), digest.Digest(imgDigest.String())) + require.NoError(t, err) + + require.NoError(t, crane.Push(img, imageTagRef.String())) + + cleanup := func() { + server.Close() + require.NoError(t, regHandler.Close()) + } + return imageTagRef, imageDigestRef, cleanup +} + +func newReference(host, repo, tag string) (reference.NamedTagged, error) { + ref, err := reference.ParseNamed(fmt.Sprintf("%s/%s", host, repo)) + if err != nil { + return nil, err + } + return reference.WithTag(ref, tag) +} + +func buildPullContext(t *testing.T, ref reference.Named) *types.SystemContext { + // Build a containers/image context that allows pulling from the test registry insecurely + registriesConf := sysregistriesv2.V2RegistriesConf{Registries: []sysregistriesv2.Registry{ + { + Prefix: reference.Domain(ref), + Endpoint: sysregistriesv2.Endpoint{ + Location: reference.Domain(ref), + Insecure: true, + }, + }, + }} + configDir := t.TempDir() + registriesConfPath := filepath.Join(configDir, "registries.conf") + f, err := os.Create(registriesConfPath) + require.NoError(t, err) + + enc := toml.NewEncoder(f) + require.NoError(t, enc.Encode(registriesConf)) + require.NoError(t, f.Close()) + + return &types.SystemContext{ + SystemRegistriesConfPath: registriesConfPath, + } +} diff --git a/internal/rukpak/source/image_registry.go b/internal/rukpak/source/image_registry.go deleted file mode 100644 index 80233f7e64..0000000000 --- a/internal/rukpak/source/image_registry.go +++ /dev/null @@ -1,197 +0,0 @@ -package source - -import ( - "archive/tar" - "context" - "crypto/tls" - "errors" - "fmt" - "io/fs" - "net/http" - "os" - "path/filepath" - "strings" - - "github.com/containerd/containerd/archive" - "github.com/google/go-containerregistry/pkg/name" - "github.com/google/go-containerregistry/pkg/v1/remote" - apimacherrors "k8s.io/apimachinery/pkg/util/errors" - "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/operator-framework/operator-controller/internal/httputil" -) - -// SourceTypeImage is the identifier for image-type bundle sources -const SourceTypeImage SourceType = "image" - -type ImageSource struct { - // Ref contains the reference to a container image containing Bundle contents. - Ref string - // InsecureSkipTLSVerify indicates that TLS certificate validation should be skipped. - // If this option is specified, the HTTPS protocol will still be used to - // fetch the specified image reference. - // This should not be used in a production environment. - InsecureSkipTLSVerify bool -} - -// Unrecoverable represents an error that can not be recovered -// from without user intervention. When this error is returned -// the request should not be requeued. -type Unrecoverable struct { - error -} - -func NewUnrecoverable(err error) *Unrecoverable { - return &Unrecoverable{err} -} - -// TODO: Make asynchronous - -type ImageRegistry struct { - BaseCachePath string - CertPoolWatcher *httputil.CertPoolWatcher -} - -func (i *ImageRegistry) Unpack(ctx context.Context, bundle *BundleSource) (*Result, error) { - l := log.FromContext(ctx) - if bundle.Type != SourceTypeImage { - panic(fmt.Sprintf("programmer error: source type %q is unable to handle specified bundle source type %q", SourceTypeImage, bundle.Type)) - } - - if bundle.Image == nil { - return nil, NewUnrecoverable(fmt.Errorf("error parsing bundle, bundle %s has a nil image source", bundle.Name)) - } - - imgRef, err := name.ParseReference(bundle.Image.Ref) - if err != nil { - return nil, NewUnrecoverable(fmt.Errorf("error parsing image reference: %w", err)) - } - - transport := remote.DefaultTransport.(*http.Transport).Clone() - if transport.TLSClientConfig == nil { - transport.TLSClientConfig = &tls.Config{ - InsecureSkipVerify: false, - MinVersion: tls.VersionTLS12, - } // nolint:gosec - } - if bundle.Image.InsecureSkipTLSVerify { - transport.TLSClientConfig.InsecureSkipVerify = true // nolint:gosec - } - if i.CertPoolWatcher != nil { - pool, _, err := i.CertPoolWatcher.Get() - if err != nil { - return nil, err - } - transport.TLSClientConfig.RootCAs = pool - } - - remoteOpts := []remote.Option{} - remoteOpts = append(remoteOpts, remote.WithTransport(transport)) - - digest, isDigest := imgRef.(name.Digest) - if isDigest { - hexVal := strings.TrimPrefix(digest.DigestStr(), "sha256:") - unpackPath := filepath.Join(i.BaseCachePath, bundle.Name, hexVal) - if stat, err := os.Stat(unpackPath); err == nil && stat.IsDir() { - l.V(1).Info("found image in filesystem cache", "digest", hexVal) - return unpackedResult(os.DirFS(unpackPath), bundle, digest.String()), nil - } - } - - // always fetch the hash - imgDesc, err := remote.Head(imgRef, remoteOpts...) - if err != nil { - return nil, fmt.Errorf("error fetching image descriptor: %w", err) - } - l.V(1).Info("resolved image descriptor", "digest", imgDesc.Digest.String()) - - unpackPath := filepath.Join(i.BaseCachePath, bundle.Name, imgDesc.Digest.Hex) - if _, err = os.Stat(unpackPath); errors.Is(err, os.ErrNotExist) { //nolint: nestif - // Ensure any previous unpacked bundle is cleaned up before unpacking the new catalog. - if err := i.Cleanup(ctx, bundle); err != nil { - return nil, fmt.Errorf("error cleaning up bundle cache: %w", err) - } - - if err = os.MkdirAll(unpackPath, 0700); err != nil { - return nil, fmt.Errorf("error creating unpack path: %w", err) - } - - if err = unpackImage(ctx, imgRef, unpackPath, remoteOpts...); err != nil { - cleanupErr := os.RemoveAll(unpackPath) - if cleanupErr != nil { - err = apimacherrors.NewAggregate( - []error{ - err, - fmt.Errorf("error cleaning up unpack path after unpack failed: %w", cleanupErr), - }, - ) - } - return nil, wrapUnrecoverable(fmt.Errorf("error unpacking image: %w", err), isDigest) - } - } else if err != nil { - return nil, fmt.Errorf("error checking if image is in filesystem cache: %w", err) - } - - resolvedRef := fmt.Sprintf("%s@sha256:%s", imgRef.Context().Name(), imgDesc.Digest.Hex) - return unpackedResult(os.DirFS(unpackPath), bundle, resolvedRef), nil -} - -func wrapUnrecoverable(err error, isUnrecoverable bool) error { - if isUnrecoverable { - return NewUnrecoverable(err) - } - return err -} - -func (i *ImageRegistry) Cleanup(_ context.Context, bundle *BundleSource) error { - return os.RemoveAll(filepath.Join(i.BaseCachePath, bundle.Name)) -} - -func unpackedResult(fsys fs.FS, bundle *BundleSource, ref string) *Result { - return &Result{ - Bundle: fsys, - ResolvedSource: &BundleSource{ - Type: SourceTypeImage, - Image: &ImageSource{ - Ref: ref, - InsecureSkipTLSVerify: bundle.Image.InsecureSkipTLSVerify, - }, - }, - State: StateUnpacked, - } -} - -// unpackImage unpacks a bundle image reference to the provided unpackPath, -// returning an error if any errors are encountered along the way. -func unpackImage(ctx context.Context, imgRef name.Reference, unpackPath string, remoteOpts ...remote.Option) error { - img, err := remote.Image(imgRef, remoteOpts...) - if err != nil { - return fmt.Errorf("error fetching remote image %q: %w", imgRef.Name(), err) - } - - layers, err := img.Layers() - if err != nil { - return fmt.Errorf("error getting image layers: %w", err) - } - - for _, layer := range layers { - layerRc, err := layer.Uncompressed() - if err != nil { - return fmt.Errorf("error getting uncompressed layer data: %w", err) - } - - // This filter ensures that the files created have the proper UID and GID - // for the filesystem they will be stored on to ensure no permission errors occur when attempting to create the - // files. - _, err = archive.Apply(ctx, unpackPath, layerRc, archive.WithFilter(func(th *tar.Header) (bool, error) { - th.Uid = os.Getuid() - th.Gid = os.Getgid() - return true, nil - })) - if err != nil { - return fmt.Errorf("error applying layer to archive: %w", err) - } - } - - return nil -} diff --git a/internal/rukpak/source/image_registry_test.go b/internal/rukpak/source/image_registry_test.go deleted file mode 100644 index 4cbd1d05b8..0000000000 --- a/internal/rukpak/source/image_registry_test.go +++ /dev/null @@ -1,360 +0,0 @@ -package source_test - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "net/http" - "net/http/httptest" - "net/url" - "os" - "path/filepath" - "testing" - - "github.com/google/go-containerregistry/pkg/crane" - "github.com/google/go-containerregistry/pkg/name" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/operator-framework/operator-controller/internal/rukpak/source" -) - -const ( - testFilePathBase string = ".image-registry-test" - bogusDigestHex string = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" - testImageTag string = "test-tag" - testImageName string = "test-image" - badImageName string = "bad-image" - testFileName string = "test-file" - testFileContents string = "test-content" -) - -func newReference(host, repo, ref string) (name.Reference, error) { - tag, err := name.NewTag(fmt.Sprintf("%s/%s:%s", host, repo, ref), name.WeakValidation) - if err == nil { - return tag, nil - } - return name.NewDigest(fmt.Sprintf("%s/%s@%s", host, repo, ref), name.WeakValidation) -} - -func imageToRawManifest(img v1.Image) ([]byte, error) { - manifest, err := img.Manifest() - if err != nil { - return nil, err - } - - layers, err := img.Layers() - if err != nil { - return nil, err - } - - rc, err := layers[0].Compressed() - if err != nil { - return nil, err - } - - lb, err := io.ReadAll(rc) - if err != nil { - return nil, err - } - - manifest.Layers[0].Data = lb - rawManifest, err := json.Marshal(manifest) - if err != nil { - return nil, err - } - - return rawManifest, nil -} - -// Adapted from: https://github.com/google/go-containerregistry/blob/main/pkg/v1/remote/image_test.go -// serveImageManifest starts a primitive image registry server hosting two images: "test-image" and "bad-image". -func serveImageManifest(rawManifest []byte) *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/v2/": - w.WriteHeader(http.StatusOK) - case fmt.Sprintf("/v2/%s/manifests/%s", testImageName, testImageTag): - if r.Method == http.MethodHead { - w.Header().Set("Content-Length", fmt.Sprint(len(rawManifest))) - w.Header().Set("Docker-Content-Digest", fmt.Sprintf("sha256:%s", bogusDigestHex)) - w.WriteHeader(http.StatusOK) - } else if r.Method != http.MethodGet { - w.WriteHeader(http.StatusBadRequest) - } - _, err := w.Write(rawManifest) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } - case fmt.Sprintf("/v2/%s/manifests/%s", badImageName, testImageTag): - case fmt.Sprintf("/v2/%s/manifests/sha256:%s", badImageName, bogusDigestHex): - if r.Method == http.MethodHead { - w.Header().Set("Content-Length", fmt.Sprint(len(rawManifest))) - w.Header().Set("Docker-Content-Digest", fmt.Sprintf("sha256:%s", bogusDigestHex)) - // We must set Content-Type since we're returning empty data below - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.WriteHeader(http.StatusOK) - return - } else if r.Method != http.MethodGet { - w.WriteHeader(http.StatusBadRequest) - } - _, err := w.Write(make([]byte, 0)) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } - default: - w.WriteHeader(http.StatusBadRequest) - } - })) -} - -func testFileCleanup() { - if _, err := os.Stat(testFilePathBase); err != nil && errors.Is(err, os.ErrNotExist) { - // Nothing to clean up - return - } else if err != nil { - log.Fatalf("error occurred locating unpack folder in post-test cleanup: %v", err) - } - // Ensure permissions and remove the temporary directory - err := os.Chmod(testFilePathBase, os.ModePerm) - if err != nil { - log.Fatalf("error occurred ensuring unpack folder permissions in post-test cleanup: %v", err) - } - err = os.RemoveAll(testFilePathBase) - if err != nil { - log.Fatalf("error occurred deleting unpack folder in post-test cleanup: %v", err) - } -} - -var HostedImageReference name.Reference - -func TestMain(m *testing.M) { - // Generate an image with file contents - img, err := crane.Image(map[string][]byte{testFileName: []byte(testFileContents)}) - if err != nil { - log.Fatalf("failed to generate image for test") - } - // Create a raw bytes manifest from the image - rawManifest, err := imageToRawManifest(img) - if err != nil { - log.Fatalf("failed to generate manifest from image") - } - - // Start the image registry and serve the generated image manifest - server := serveImageManifest(rawManifest) - if err != nil { - log.Fatalf("image registry server failed to start") - } - u, err := url.Parse(server.URL) - if err != nil { - log.Fatalf("invalid server URL from image registry") - } - HostedImageReference, err = newReference(u.Host, testImageName, testImageTag) - if err != nil { - log.Fatalf("failed to generate image reference for served image") - } - code := m.Run() - server.Close() - os.Exit(code) -} - -func TestUnpackValidInsecure(t *testing.T) { - defer testFileCleanup() - unpacker := &source.ImageRegistry{ - BaseCachePath: testFilePathBase, - } - bundleSource := &source.BundleSource{ - Type: source.SourceTypeImage, - Image: &source.ImageSource{ - Ref: HostedImageReference.String(), - InsecureSkipTLSVerify: true, - }, - } - - unpackPath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, bogusDigestHex) - - // Create another folder to simulate an old unpacked bundle - oldBundlePath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, "foo") - require.NoError(t, os.MkdirAll(oldBundlePath, os.ModePerm)) - - // Attempt to pull and unpack the image - result, err := unpacker.Unpack(context.Background(), bundleSource) - // Check Result - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, result.State, source.StateUnpacked) - // Make sure the old bundle was cleaned up - require.NoDirExists(t, oldBundlePath) - - // Give permissions to read the file - require.NoError(t, os.Chmod(filepath.Join(unpackPath, testFileName), 0400)) - unpackedFile, err := os.ReadFile(filepath.Join(unpackPath, testFileName)) - require.NoError(t, err) - // Ensure the unpacked file matches the source content - require.Equal(t, []byte(testFileContents), unpackedFile) -} - -func TestUnpackValidUsesCache(t *testing.T) { - defer testFileCleanup() - unpacker := &source.ImageRegistry{ - BaseCachePath: testFilePathBase, - } - bundleSource := &source.BundleSource{ - Type: source.SourceTypeImage, - Image: &source.ImageSource{ - Ref: fmt.Sprintf("%s@sha256:%s", HostedImageReference.Context().Name(), bogusDigestHex), - InsecureSkipTLSVerify: true, - }, - } - - // Populate the bundle cache with a folder - testCacheFilePath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, bogusDigestHex, "test-folder") - require.NoError(t, os.MkdirAll(testCacheFilePath, os.ModePerm)) - - // Attempt to pull and unpack the image - result, err := unpacker.Unpack(context.Background(), bundleSource) - // Check Result - require.NoError(t, err) - require.NotNil(t, result) - assert.Equal(t, result.State, source.StateUnpacked) - // Make sure the old file was not cleaned up - require.DirExists(t, testCacheFilePath) -} - -func TestUnpackCacheCheckError(t *testing.T) { - defer testFileCleanup() - unpacker := &source.ImageRegistry{ - BaseCachePath: testFilePathBase, - } - bundleSource := &source.BundleSource{ - Type: source.SourceTypeImage, - Image: &source.ImageSource{ - Ref: HostedImageReference.String(), - InsecureSkipTLSVerify: true, - }, - } - - // Create the unpack path and restrict its permissions - unpackPath := filepath.Join(unpacker.BaseCachePath, bundleSource.Name, bogusDigestHex) - require.NoError(t, os.MkdirAll(unpackPath, os.ModePerm)) - require.NoError(t, os.Chmod(testFilePathBase, 0000)) - // Attempt to pull and unpack the image - _, err := unpacker.Unpack(context.Background(), bundleSource) - // Check Result - require.Error(t, err) -} - -func TestUnpackUnservedImageReference(t *testing.T) { - defer testFileCleanup() - unpacker := &source.ImageRegistry{ - BaseCachePath: testFilePathBase, - } - bundleSource := &source.BundleSource{ - Type: source.SourceTypeImage, - Image: &source.ImageSource{ - // Use a valid reference that is not served - Ref: fmt.Sprintf("%s/%s:unserved-tag", HostedImageReference.Context().Registry.RegistryStr(), badImageName), - InsecureSkipTLSVerify: true, - }, - } - - // Attempt to pull and unpack the image - _, err := unpacker.Unpack(context.Background(), bundleSource) - // Check Result - require.Error(t, err) -} - -func TestUnpackFailure(t *testing.T) { - defer testFileCleanup() - unpacker := &source.ImageRegistry{ - BaseCachePath: testFilePathBase, - } - bundleSource := &source.BundleSource{ - Type: source.SourceTypeImage, - Image: &source.ImageSource{ - // Use a valid reference that is served but will return bad image content - Ref: fmt.Sprintf("%s/%s:%s", HostedImageReference.Context().Registry.RegistryStr(), badImageName, testImageTag), - InsecureSkipTLSVerify: true, - }, - } - - // Attempt to pull and unpack the image - _, err := unpacker.Unpack(context.Background(), bundleSource) - // Check Result - require.Error(t, err) -} - -func TestUnpackFailureDigest(t *testing.T) { - defer testFileCleanup() - unpacker := &source.ImageRegistry{ - BaseCachePath: testFilePathBase, - } - bundleSource := &source.BundleSource{ - Type: source.SourceTypeImage, - Image: &source.ImageSource{ - // Use a valid reference that is served but will return bad image content - Ref: fmt.Sprintf("%s/%s@sha256:%s", HostedImageReference.Context().Registry.RegistryStr(), badImageName, bogusDigestHex), - InsecureSkipTLSVerify: true, - }, - } - - // Attempt to pull and unpack the image - _, err := unpacker.Unpack(context.Background(), bundleSource) - // Check Result - require.Error(t, err) - // Unpacker gives an error of type Unrecoverable - require.IsType(t, &source.Unrecoverable{}, err) -} - -func TestUnpackInvalidSourceType(t *testing.T) { - unpacker := &source.ImageRegistry{} - // Create BundleSource with invalid source type - bundleSource := &source.BundleSource{ - Type: "invalid", - } - - shouldPanic := func() { - // Attempt to pull and unpack the image - _, err := unpacker.Unpack(context.Background(), bundleSource) - if err != nil { - t.Error("func should have panicked") - } - } - require.Panics(t, shouldPanic) -} - -func TestUnpackInvalidNilImage(t *testing.T) { - unpacker := &source.ImageRegistry{} - // Create BundleSource with nil Image - bundleSource := &source.BundleSource{ - Type: source.SourceTypeImage, - Image: nil, - } - - // Attempt to unpack - result, err := unpacker.Unpack(context.Background(), bundleSource) - require.Error(t, err) - require.NoDirExists(t, testFilePathBase) - assert.Nil(t, result) -} - -func TestUnpackInvalidImageRef(t *testing.T) { - unpacker := &source.ImageRegistry{} - // Create BundleSource with malformed image reference - bundleSource := &source.BundleSource{ - Type: source.SourceTypeImage, - Image: &source.ImageSource{ - Ref: "invalid image ref", - }, - } - - // Attempt to unpack - result, err := unpacker.Unpack(context.Background(), bundleSource) - require.Error(t, err) - require.NoDirExists(t, testFilePathBase) - assert.Nil(t, result) -} diff --git a/internal/rukpak/source/unpacker.go b/internal/rukpak/source/unpacker.go index e98b92a68e..5d2b5f633d 100644 --- a/internal/rukpak/source/unpacker.go +++ b/internal/rukpak/source/unpacker.go @@ -2,10 +2,28 @@ package source import ( "context" - "fmt" "io/fs" ) +// SourceTypeImage is the identifier for image-type bundle sources +const SourceTypeImage SourceType = "image" + +type ImageSource struct { + // Ref contains the reference to a container image containing Bundle contents. + Ref string +} + +// Unrecoverable represents an error that can not be recovered +// from without user intervention. When this error is returned +// the request should not be requeued. +type Unrecoverable struct { + error +} + +func NewUnrecoverable(err error) Unrecoverable { + return Unrecoverable{err} +} + // Unpacker unpacks bundle content, either synchronously or asynchronously and // returns a Result, which conveys information about the progress of unpacking // the bundle content. @@ -74,29 +92,3 @@ type BundleSource struct { // Image is the bundle image that backs the content of this bundle. Image *ImageSource } - -type unpacker struct { - sources map[SourceType]Unpacker -} - -// NewUnpacker returns a new composite Source that unpacks bundles using the source -// mapping provided by the configured sources. -func NewUnpacker(sources map[SourceType]Unpacker) Unpacker { - return &unpacker{sources: sources} -} - -func (s *unpacker) Unpack(ctx context.Context, bundle *BundleSource) (*Result, error) { - source, ok := s.sources[bundle.Type] - if !ok { - return nil, fmt.Errorf("source type %q not supported", bundle.Type) - } - return source.Unpack(ctx, bundle) -} - -func (s *unpacker) Cleanup(ctx context.Context, bundle *BundleSource) error { - source, ok := s.sources[bundle.Type] - if !ok { - return fmt.Errorf("source type %q not supported", bundle.Type) - } - return source.Cleanup(ctx, bundle) -} diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 638539ff74..ba568c6074 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -274,7 +274,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "unpack successful") + assert.Regexp(ct, "^unpacked .* successfully", cond.Message) }, pollDuration, pollInterval) t.Log("By eventually installing the package successfully") @@ -758,6 +758,7 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "Installed bundle") assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.2.0", @@ -849,7 +850,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "unpack successful") + assert.Regexp(ct, "^unpacked .* successfully", cond.Message) }, pollDuration, pollInterval) t.Log("By eventually failing to install the package successfully due to insufficient ServiceAccount permissions") From 5f0b5f044adba1a2aca78e51c5336a8d504a4186 Mon Sep 17 00:00:00 2001 From: Brett Tofel Date: Thu, 12 Sep 2024 15:56:59 -0400 Subject: [PATCH 003/694] =?UTF-8?q?=F0=9F=93=96=20[Docs]=20Single=20Cluste?= =?UTF-8?q?rExtension=20ownership=20concept=20(#1258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add single-owner-objects.md Signed-off-by: Brett Tofel * Replace openshift ref Signed-off-by: Brett Tofel * Update docs/drafts/single-owner-objects.md Co-authored-by: Per Goncalves da Silva * Clarify bundle ClusterExtension relationship Signed-off-by: Brett Tofel * Custom resource for object at top of doc Signed-off-by: Brett Tofel * Custom resource for object everywhere Signed-off-by: Brett Tofel * Update docs/drafts/single-owner-objects.md Co-authored-by: Bryce Palmer --------- Signed-off-by: Brett Tofel Co-authored-by: Per Goncalves da Silva Co-authored-by: Bryce Palmer --- docs/drafts/single-owner-objects.md | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/drafts/single-owner-objects.md diff --git a/docs/drafts/single-owner-objects.md b/docs/drafts/single-owner-objects.md new file mode 100644 index 0000000000..0ed7dfcac5 --- /dev/null +++ b/docs/drafts/single-owner-objects.md @@ -0,0 +1,34 @@ + +# OLM Ownership Enforcement for `ClusterExtensions` + +In OLM, **a Kubernetes resource can only be owned by a single `ClusterExtension` at a time**. This ensures that resources within a Kubernetes cluster are managed consistently and prevents conflicts between multiple `ClusterExtensions` attempting to control the same resource. + +## Key Concept: Single Ownership + +The core principle enforced by OLM is that each resource can only have one `ClusterExtension` as its owner. This prevents overlapping or conflicting management by multiple `ClusterExtensions`, ensuring that each resource is uniquely associated with only one operator bundle. + +## Implications of Single Ownership + +### 1. Operator Bundles That Provide a CRD Can Only Be Installed Once + +Operator bundles provide `CustomResourceDefinitions` (CRDs), which are part of a `ClusterExtension`. This means a bundle can only be installed once in a cluster. Attempting to install another bundle that provides the same CRDs will result in a failure, as each custom resource can have only one `ClusterExtension` as its owner. + + +### 2. `ClusterExtensions` Cannot Share Objects +OLM's single-owner policy means that **`ClusterExtensions` cannot share ownership of any resources**. If one `ClusterExtension` manages a specific resource (e.g., a `Deployment`, `CustomResourceDefinition`, or `Service`), another `ClusterExtension` cannot claim ownership of the same resource. Any attempt to do so will be blocked by the system. + +## Error Messages + +When a conflict occurs due to multiple `ClusterExtensions` attempting to manage the same resource, `operator-controller` will return a clear error message, indicating the ownership conflict. + +- **Example Error**: + ```plaintext + CustomResourceDefinition 'logfilemetricexporters.logging.kubernetes.io' already exists in namespace 'kubernetes-logging' and cannot be managed by operator-controller + ``` + +This error message signals that the resource is already being managed by another `ClusterExtension` and cannot be reassigned or "shared." + +## What This Means for You + +- **Uniqueness of Operator Bundles**: Ensure that operator bundles providing the same CRDs are not installed more than once. This can prevent potential installation failures due to ownership conflicts. +- **Avoid Resource Sharing**: If you need different `ClusterExtensions` to interact with similar resources, ensure they are managing separate resources. `ClusterExtensions` cannot jointly manage the same resource due to the single-owner enforcement. From 3e2a8840ed44cb5a19cf335b3c0156d51859f1d0 Mon Sep 17 00:00:00 2001 From: Daniel Franz Date: Thu, 12 Sep 2024 13:14:58 -0700 Subject: [PATCH 004/694] catalogd API Reference Generation (#1240) Generates the catalogd API reference along with the operator-controller's Signed-off-by: dtfranz --- .github/workflows/sanity.yaml | 27 ++- Makefile | 17 +- docs/refs/api/catalogd-api-reference.md | 192 ++++++++++++++++++ docs/refs/api/crd-ref-docs-gen-config.yaml | 7 + .../api/operator-controller-api-reference.md | 8 +- mkdocs.yml | 3 +- 6 files changed, 236 insertions(+), 18 deletions(-) create mode 100644 docs/refs/api/catalogd-api-reference.md create mode 100644 docs/refs/api/crd-ref-docs-gen-config.yaml diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml index 5b7a641a36..1c34818cd0 100644 --- a/.github/workflows/sanity.yaml +++ b/.github/workflows/sanity.yaml @@ -12,13 +12,26 @@ jobs: verify: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - go-version-file: "go.mod" - - name: Run verification checks - run: make verify + - uses: actions/checkout@v4 + with: + path: operator-controller + - uses: actions/setup-go@v5 + with: + go-version-file: "operator-controller/go.mod" + - name: Get catalogd version + id: get-catalogd-version + run: | + cd operator-controller + echo "CATALOGD_VERSION=$(go list -mod=mod -m -f "{{.Version}}" github.com/operator-framework/catalogd)" >> "$GITHUB_OUTPUT" + - uses: actions/checkout@v4 + with: + repository: operator-framework/catalogd + path: catalogd + ref: "${{ steps.get-catalogd-version.outputs.CATALOGD_VERSION }}" + - name: Run verification checks + run: | + cd operator-controller + make verify lint: runs-on: ubuntu-latest steps: diff --git a/Makefile b/Makefile index f92abb7ca0..afca4ce824 100644 --- a/Makefile +++ b/Makefile @@ -309,13 +309,18 @@ quickstart: $(KUSTOMIZE) manifests #EXHELP Generate the installation release man ##@ Docs .PHONY: crd-ref-docs -API_REFERENCE_FILENAME := operator-controller-api-reference.md -crd-ref-docs: $(CRD_REF_DOCS) - rm -f $(ROOT_DIR)/docs/refs/api/$(API_REFERENCE_FILENAME) +OPERATOR_CONTROLLER_API_REFERENCE_FILENAME := operator-controller-api-reference.md +CATALOGD_API_REFERENCE_FILENAME := catalogd-api-reference.md +CATALOGD_PATH := "$(ROOT_DIR)/../catalogd" +crd-ref-docs: $(CRD_REF_DOCS) #EXHELP Generate the API Reference Documents. NOTE: catalogd must be cloned into operator-controller's parent directory. + rm -f $(ROOT_DIR)/docs/refs/api/$(OPERATOR_CONTROLLER_API_REFERENCE_FILENAME) $(CRD_REF_DOCS) --source-path=$(ROOT_DIR)/api \ - --config=$(ROOT_DIR)/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml \ - --renderer=markdown \ - --output-path=$(ROOT_DIR)/docs/refs/api/$(API_REFERENCE_FILENAME) + --config=$(ROOT_DIR)/docs/refs/api/crd-ref-docs-gen-config.yaml \ + --renderer=markdown --output-path=$(ROOT_DIR)/docs/refs/api/$(OPERATOR_CONTROLLER_API_REFERENCE_FILENAME); + rm -f $(ROOT_DIR)/docs/refs/api/$(CATALOGD_API_REFERENCE_FILENAME) + $(CRD_REF_DOCS) --source-path=$(CATALOGD_PATH)/api \ + --config=$(ROOT_DIR)/docs/refs/api/crd-ref-docs-gen-config.yaml \ + --renderer=markdown --output-path=$(ROOT_DIR)/docs/refs/api/$(CATALOGD_API_REFERENCE_FILENAME) VENVDIR := $(abspath docs/.venv) diff --git a/docs/refs/api/catalogd-api-reference.md b/docs/refs/api/catalogd-api-reference.md new file mode 100644 index 0000000000..89d130a9fa --- /dev/null +++ b/docs/refs/api/catalogd-api-reference.md @@ -0,0 +1,192 @@ +# API Reference + +## Packages +- [olm.operatorframework.io/core](#olmoperatorframeworkiocore) +- [olm.operatorframework.io/v1alpha1](#olmoperatorframeworkiov1alpha1) + + +## olm.operatorframework.io/core + +Package api is the internal version of the API. + + + + +## olm.operatorframework.io/v1alpha1 + +Package v1alpha1 contains API Schema definitions for the core v1alpha1 API group + +### Resource Types +- [ClusterCatalog](#clustercatalog) +- [ClusterCatalogList](#clustercataloglist) + + + +#### CatalogSource + + + +CatalogSource contains the sourcing information for a Catalog + + + +_Appears in:_ +- [ClusterCatalogSpec](#clustercatalogspec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `type` _[SourceType](#sourcetype)_ | type defines the kind of Catalog content being sourced. | | Enum: [image]
Required: \{\}
| +| `image` _[ImageSource](#imagesource)_ | image is the catalog image that backs the content of this catalog. | | | + + +#### ClusterCatalog + + + +ClusterCatalog is the Schema for the ClusterCatalogs API + + + +_Appears in:_ +- [ClusterCatalogList](#clustercataloglist) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `apiVersion` _string_ | `olm.operatorframework.io/v1alpha1` | | | +| `kind` _string_ | `ClusterCatalog` | | | +| `kind` _string_ | 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 | | | +| `apiVersion` _string_ | 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 | | | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[ClusterCatalogSpec](#clustercatalogspec)_ | | | | +| `status` _[ClusterCatalogStatus](#clustercatalogstatus)_ | | | | + + +#### ClusterCatalogList + + + +ClusterCatalogList contains a list of ClusterCatalog + + + + + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `apiVersion` _string_ | `olm.operatorframework.io/v1alpha1` | | | +| `kind` _string_ | `ClusterCatalogList` | | | +| `kind` _string_ | 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 | | | +| `apiVersion` _string_ | 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 | | | +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[ClusterCatalog](#clustercatalog) array_ | | | | + + +#### ClusterCatalogSpec + + + +ClusterCatalogSpec defines the desired state of ClusterCatalog + + + +_Appears in:_ +- [ClusterCatalog](#clustercatalog) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `source` _[CatalogSource](#catalogsource)_ | source is the source of a Catalog that contains catalog metadata in the FBC format
https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs | | | +| `priority` _integer_ | priority is used as the tie-breaker between bundles selected from different catalogs; a higher number means higher priority. | 0 | | + + +#### ClusterCatalogStatus + + + +ClusterCatalogStatus defines the observed state of ClusterCatalog + + + +_Appears in:_ +- [ClusterCatalog](#clustercatalog) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions store the status conditions of the ClusterCatalog instances | | | +| `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source | | | +| `contentURL` _string_ | contentURL is a cluster-internal address that on-cluster components
can read the content of a catalog from | | | +| `observedGeneration` _integer_ | observedGeneration is the most recent generation observed for this ClusterCatalog. It corresponds to the
ClusterCatalog's generation, which is updated on mutation by the API Server. | | | +| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | LastUnpacked represents the time when the
ClusterCatalog object was last unpacked. | | | + + +#### ImageSource + + + +ImageSource contains information required for sourcing a Catalog from an OCI image + + + +_Appears in:_ +- [CatalogSource](#catalogsource) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `ref` _string_ | ref contains the reference to a container image containing Catalog contents. | | | +| `pullSecret` _string_ | pullSecret contains the name of the image pull secret in the namespace that catalogd is deployed. | | | +| `pollInterval` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#duration-v1-meta)_ | pollInterval indicates the interval at which the image source should be polled for new content,
specified as a duration (e.g., "5m", "1h", "24h", "etc".). Note that PollInterval may not be
specified for a catalog image referenced by a sha256 digest. | | Format: duration
| +| `insecureSkipTLSVerify` _boolean_ | insecureSkipTLSVerify indicates that TLS certificate validation should be skipped.
If this option is specified, the HTTPS protocol will still be used to
fetch the specified image reference.
This should not be used in a production environment. | | | + + +#### ResolvedCatalogSource + + + +ResolvedCatalogSource contains the information about a sourced Catalog + + + +_Appears in:_ +- [ClusterCatalogStatus](#clustercatalogstatus) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `type` _[SourceType](#sourcetype)_ | type defines the kind of Catalog content that was sourced. | | Enum: [image]
Required: \{\}
| +| `image` _[ResolvedImageSource](#resolvedimagesource)_ | image is the catalog image that backs the content of this catalog. | | | + + +#### ResolvedImageSource + + + +ResolvedImageSource contains information about the sourced Catalog + + + +_Appears in:_ +- [ResolvedCatalogSource](#resolvedcatalogsource) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `ref` _string_ | ref contains the reference to a container image containing Catalog contents. | | | +| `resolvedRef` _string_ | resolvedRef contains the resolved sha256 image ref containing Catalog contents. | | | +| `lastPollAttempt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastPollAtempt is the time when the source resolved was last polled for new content. | | | +| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | LastUnpacked is the time when the catalog contents were successfully unpacked. | | | + + +#### SourceType + +_Underlying type:_ _string_ + + + + + +_Appears in:_ +- [CatalogSource](#catalogsource) +- [ResolvedCatalogSource](#resolvedcatalogsource) + +| Field | Description | +| --- | --- | +| `image` | | + + diff --git a/docs/refs/api/crd-ref-docs-gen-config.yaml b/docs/refs/api/crd-ref-docs-gen-config.yaml new file mode 100644 index 0000000000..f6394fdf61 --- /dev/null +++ b/docs/refs/api/crd-ref-docs-gen-config.yaml @@ -0,0 +1,7 @@ +processor: + ignoreTypes: [] + ignoreFields: [] + +render: + # Version of Kubernetes to use when generating links to Kubernetes API documentation. + kubernetesVersion: 1.31 diff --git a/docs/refs/api/operator-controller-api-reference.md b/docs/refs/api/operator-controller-api-reference.md index d03bc4df4e..de0a1f2bd1 100644 --- a/docs/refs/api/operator-controller-api-reference.md +++ b/docs/refs/api/operator-controller-api-reference.md @@ -81,7 +81,7 @@ _Appears in:_ | `packageName` _string_ | packageName is a reference to the name of the package to be installed
and is used to filter the content from catalogs.

This field is required, immutable and follows the DNS subdomain name
standard as defined in [RFC 1123]. This means that valid entries:
- Contain no more than 253 characters
- Contain only lowercase alphanumeric characters, '-', or '.'
- Start with an alphanumeric character
- End with an alphanumeric character

Some examples of valid values are:
- some-package
- 123-package
- 1-package-2
- somepackage

Some examples of invalid values are:
- -some-package
- some-package-
- thisisareallylongpackagenamethatisgreaterthanthemaximumlength
- some.package

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 253
Pattern: `^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
| | `version` _string_ | version is an optional semver constraint (a specific version or range of versions). When unspecified, the latest version available will be installed.

Acceptable version ranges are no longer than 64 characters.
Version ranges are composed of comma- or space-delimited values and one or
more comparison operators, known as comparison strings. Additional
comparison strings can be added using the OR operator (\|\|).

# Range Comparisons

To specify a version range, you can use a comparison string like ">=3.0,
<3.6". When specifying a range, automatic updates will occur within that
range. The example comparison string means "install any version greater than
or equal to 3.0.0 but less than 3.6.0.". It also states intent that if any
upgrades are available within the version range after initial installation,
those upgrades should be automatically performed.

# Pinned Versions

To specify an exact version to install you can use a version range that
"pins" to a specific version. When pinning to a specific version, no
automatic updates will occur. An example of a pinned version range is
"0.6.0", which means "only install version 0.6.0 and never
upgrade from this version".

# Basic Comparison Operators

The basic comparison operators and their meanings are:
- "=", equal (not aliased to an operator)
- "!=", not equal
- "<", less than
- ">", greater than
- ">=", greater than OR equal to
- "<=", less than OR equal to

# Wildcard Comparisons

You can use the "x", "X", and "*" characters as wildcard characters in all
comparison operations. Some examples of using the wildcard characters:
- "1.2.x", "1.2.X", and "1.2.*" is equivalent to ">=1.2.0, < 1.3.0"
- ">= 1.2.x", ">= 1.2.X", and ">= 1.2.*" is equivalent to ">= 1.2.0"
- "<= 2.x", "<= 2.X", and "<= 2.*" is equivalent to "< 3"
- "x", "X", and "*" is equivalent to ">= 0.0.0"

# Patch Release Comparisons

When you want to specify a minor version up to the next major version you
can use the "~" character to perform patch comparisons. Some examples:
- "~1.2.3" is equivalent to ">=1.2.3, <1.3.0"
- "~1" and "~1.x" is equivalent to ">=1, <2"
- "~2.3" is equivalent to ">=2.3, <2.4"
- "~1.2.x" is equivalent to ">=1.2.0, <1.3.0"

# Major Release Comparisons

You can use the "^" character to make major release comparisons after a
stable 1.0.0 version is published. If there is no stable version published, // minor versions define the stability level. Some examples:
- "^1.2.3" is equivalent to ">=1.2.3, <2.0.0"
- "^1.2.x" is equivalent to ">=1.2.0, <2.0.0"
- "^2.3" is equivalent to ">=2.3, <3"
- "^2.x" is equivalent to ">=2.0.0, <3"
- "^0.2.3" is equivalent to ">=0.2.3, <0.3.0"
- "^0.2" is equivalent to ">=0.2.0, <0.3.0"
- "^0.0.3" is equvalent to ">=0.0.3, <0.0.4"
- "^0.0" is equivalent to ">=0.0.0, <0.1.0"
- "^0" is equivalent to ">=0.0.0, <1.0.0"

# OR Comparisons
You can use the "\|\|" character to represent an OR operation in the version
range. Some examples:
- ">=1.2.3, <2.0.0 \|\| >3.0.0"
- "^0 \|\| ^3 \|\| ^5"

For more information on semver, please see https://semver.org/ | | MaxLength: 64
Pattern: `^(\s*(=\|\|!=\|>\|<\|>=\|=>\|<=\|=<\|~\|~>\|\^)\s*(v?(0\|[1-9]\d*\|[x\|X\|\*])(\.(0\|[1-9]\d*\|x\|X\|\*]))?(\.(0\|[1-9]\d*\|x\|X\|\*))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)((?:\s+\|,\s*\|\s*\\|\\|\s*)(=\|\|!=\|>\|<\|>=\|=>\|<=\|=<\|~\|~>\|\^)\s*(v?(0\|[1-9]\d*\|x\|X\|\*])(\.(0\|[1-9]\d*\|x\|X\|\*))?(\.(0\|[1-9]\d*\|x\|X\|\*]))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)*$`
| | `channels` _string array_ | channels is an optional reference to a set of channels belonging to
the package specified in the packageName field.

A "channel" is a package author defined stream of updates for an extension.

When specified, it is used to constrain the set of installable bundles and
the automated upgrade path. This constraint is an AND operation with the
version field. For example:
- Given channel is set to "foo"
- Given version is set to ">=1.0.0, <1.5.0"
- Only bundles that exist in channel "foo" AND satisfy the version range comparison will be considered installable
- Automatic upgrades will be constrained to upgrade edges defined by the selected channel

When unspecified, upgrade edges across all channels will be used to identify valid automatic upgrade paths.

This field follows the DNS subdomain name standard as defined in [RFC
1123]. This means that valid entries:
- Contain no more than 253 characters
- Contain only lowercase alphanumeric characters, '-', or '.'
- Start with an alphanumeric character
- End with an alphanumeric character

Some examples of valid values are:
- 1.1.x
- alpha
- stable
- stable-v1
- v1-stable
- dev-preview
- preview
- community

Some examples of invalid values are:
- -some-channel
- some-channel-
- thisisareallylongchannelnamethatisgreaterthanthemaximumlength
- original_40
- --default-channel

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | | -| `selector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v/#labelselector-v1-meta)_ | selector is an optional field that can be used
to filter the set of ClusterCatalogs used in the bundle
selection process.

When unspecified, all ClusterCatalogs will be used in
the bundle selection process. | | | +| `selector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#labelselector-v1-meta)_ | selector is an optional field that can be used
to filter the set of ClusterCatalogs used in the bundle
selection process.

When unspecified, all ClusterCatalogs will be used in
the bundle selection process. | | | | `upgradeConstraintPolicy` _[UpgradeConstraintPolicy](#upgradeconstraintpolicy)_ | upgradeConstraintPolicy is an optional field that controls whether
the upgrade path(s) defined in the catalog are enforced for the package
referenced in the packageName field.

Allowed values are: ["CatalogProvided", "SelfCertified"].

When this field is set to "CatalogProvided", automatic upgrades will only occur
when upgrade constraints specified by the package author are met.

When this field is set to "SelfCertified", the upgrade constraints specified by
the package author are ignored. This allows for upgrades and downgrades to
any version of the package. This is considered a dangerous operation as it
can lead to unknown and potentially disastrous outcomes, such as data
loss. It is assumed that users have independently verified changes when
using this option.

If unspecified, the default value is "CatalogProvided". | CatalogProvided | Enum: [CatalogProvided SelfCertified]
| @@ -102,7 +102,7 @@ _Appears in:_ | `kind` _string_ | `ClusterExtension` | | | | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | | `spec` _[ClusterExtensionSpec](#clusterextensionspec)_ | | | | | `status` _[ClusterExtensionStatus](#clusterextensionstatus)_ | | | | @@ -158,7 +158,7 @@ ClusterExtensionList contains a list of ClusterExtension | `kind` _string_ | `ClusterExtensionList` | | | | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | | `items` _[ClusterExtension](#clusterextension) array_ | | | | @@ -210,7 +210,7 @@ _Appears in:_ | --- | --- | --- | --- | | `install` _[ClusterExtensionInstallStatus](#clusterextensioninstallstatus)_ | | | | | `resolution` _[ClusterExtensionResolutionStatus](#clusterextensionresolutionstatus)_ | | | | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterExtension.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human readable message that further elaborates on the state of the condition

The global set of condition types are:
- "Installed", represents whether or not the a bundle has been installed for this ClusterExtension
- "Resolved", represents whether or not a bundle was found that satisfies the selection criteria outlined in the spec
- "Unpacked", represents whether or not the bundle contents have been successfully unpacked

When the ClusterExtension is sourced from a catalog, the following conditions are also possible:
- "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types
- "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated
- "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated
- "BundleDeprecated", represents whether or not the installed bundle is deprecated

The current set of reasons are:
- "Success", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when unpacking a bundle's content, resolution and installation/upgrading is successful
- "Failed", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when an error has occurred while unpacking the contents of a bundle, during resolution or installation. | | | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterExtension.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human readable message that further elaborates on the state of the condition

The global set of condition types are:
- "Installed", represents whether or not the a bundle has been installed for this ClusterExtension
- "Resolved", represents whether or not a bundle was found that satisfies the selection criteria outlined in the spec
- "Unpacked", represents whether or not the bundle contents have been successfully unpacked

When the ClusterExtension is sourced from a catalog, the following conditions are also possible:
- "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types
- "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated
- "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated
- "BundleDeprecated", represents whether or not the installed bundle is deprecated

The current set of reasons are:
- "Success", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when unpacking a bundle's content, resolution and installation/upgrading is successful
- "Failed", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when an error has occurred while unpacking the contents of a bundle, during resolution or installation. | | | #### PreflightConfig diff --git a/mkdocs.yml b/mkdocs.yml index e5a4d67177..ec6ed83e6d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -19,7 +19,8 @@ nav: - References: - Supported extensions: 'refs//supported-extensions.md' - API references: - - Operator controller API reference: 'refs/api/operator-controller-api-reference.md' + - Operator Controller API reference: 'refs/api/operator-controller-api-reference.md' + - CatalogD API reference: 'refs/api/catalogd-api-reference.md' - Catalog queries: 'refs/catalog-queries.md' - CRD Upgrade Safety: 'refs/crd-upgrade-safety.md' From 90a7f2dc7874ae909c30136ac962bef62cedc0b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:19:39 +0200 Subject: [PATCH 005/694] :seedling: Bump regex from 2024.7.24 to 2024.9.11 (#1261) Bumps [regex](https://github.com/mrabarnett/mrab-regex) from 2024.7.24 to 2024.9.11. - [Changelog](https://github.com/mrabarnett/mrab-regex/blob/hg/changelog.txt) - [Commits](https://github.com/mrabarnett/mrab-regex/compare/2024.7.24...2024.9.11) --- updated-dependencies: - dependency-name: regex dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 64df72806a..70ad637f58 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,7 +27,7 @@ python-dateutil==2.9.0.post0 PyYAML==6.0.2 pyyaml_env_tag==0.1 readtime==3.0.0 -regex==2024.7.24 +regex==2024.9.11 requests==2.32.3 six==1.16.0 soupsieve==2.6 From 58d31c598ef6636f564d88bb89eea586d8d2158a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 07:30:29 +0000 Subject: [PATCH 006/694] :seedling: Bump the k8s-dependencies group with 2 updates (#1259) Bumps the k8s-dependencies group with 2 updates: [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/cli-runtime` from 0.31.0 to 0.31.1 - [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.31.0...v0.31.1) Updates `k8s.io/client-go` from 0.31.0 to 0.31.1 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.31.0...v0.31.1) --- updated-dependencies: - dependency-name: k8s.io/cli-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-dependencies - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-dependencies ... Signed-off-by: dependabot[bot] Signed-off-by: Per Goncalves da Silva Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 0829548c3c..e797b0a751 100644 --- a/go.mod +++ b/go.mod @@ -27,12 +27,12 @@ require ( golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.15.4 - k8s.io/api v0.31.0 - k8s.io/apiextensions-apiserver v0.31.0 - k8s.io/apimachinery v0.31.0 - k8s.io/cli-runtime v0.31.0 - k8s.io/client-go v0.31.0 - k8s.io/component-base v0.31.0 + k8s.io/api v0.31.1 + k8s.io/apiextensions-apiserver v0.31.1 + k8s.io/apimachinery v0.31.1 + k8s.io/cli-runtime v0.31.1 + k8s.io/client-go v0.31.1 + k8s.io/component-base v0.31.1 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 sigs.k8s.io/controller-runtime v0.19.0 sigs.k8s.io/yaml v1.4.0 @@ -243,7 +243,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.31.0 // indirect + k8s.io/apiserver v0.31.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/kubectl v0.31.0 // indirect diff --git a/go.sum b/go.sum index 17275b18cc..a5f224fb81 100644 --- a/go.sum +++ b/go.sum @@ -1034,20 +1034,20 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= -k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= -k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= -k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= -k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= -k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= -k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= -k8s.io/cli-runtime v0.31.0 h1:V2Q1gj1u3/WfhD475HBQrIYsoryg/LrhhK4RwpN+DhA= -k8s.io/cli-runtime v0.31.0/go.mod h1:vg3H94wsubuvWfSmStDbekvbla5vFGC+zLWqcf+bGDw= -k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= -k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= -k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= -k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= +k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= +k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= +k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= +k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= +k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= +k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= +k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk= +k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U= +k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= +k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= +k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= From 633ca72458619a8af13392059a4946f72f4f9e51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:05:48 +0200 Subject: [PATCH 007/694] :seedling: Bump urllib3 from 2.2.2 to 2.2.3 (#1262) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.2 to 2.2.3. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.2.2...2.2.3) --- updated-dependencies: - dependency-name: urllib3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 70ad637f58..1189e78710 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,5 +31,5 @@ regex==2024.9.11 requests==2.32.3 six==1.16.0 soupsieve==2.6 -urllib3==2.2.2 +urllib3==2.2.3 watchdog==4.0.2 From f0c761d8955cb01e662056a807a095fccd6e1d5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:06:09 +0200 Subject: [PATCH 008/694] :seedling: Bump helm.sh/helm/v3 from 3.15.4 to 3.16.1 (#1265) Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.15.4 to 3.16.1. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.15.4...v3.16.1) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 17 ++++++------ go.sum | 85 +++++++++++++++------------------------------------------- 2 files changed, 30 insertions(+), 72 deletions(-) diff --git a/go.mod b/go.mod index e797b0a751..e241b05313 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 gopkg.in/yaml.v2 v2.4.0 - helm.sh/helm/v3 v3.15.4 + helm.sh/helm/v3 v3.16.1 k8s.io/api v0.31.1 k8s.io/apiextensions-apiserver v0.31.1 k8s.io/apimachinery v0.31.1 @@ -40,11 +40,12 @@ require ( require ( carvel.dev/vendir v0.40.0 // indirect + dario.cat/mergo v1.0.1 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.5 // indirect @@ -84,7 +85,7 @@ require ( github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.11.2 // indirect - github.com/evanphx/json-patch v5.7.0+incompatible // indirect + github.com/evanphx/json-patch v5.9.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.15.0 // indirect @@ -130,11 +131,11 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/huandu/xstrings v1.4.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect + github.com/jmoiron/sqlx v1.4.0 // indirect github.com/joelanford/ignore v0.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -185,15 +186,15 @@ require ( github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rubenv/sql-migrate v1.5.2 // indirect + github.com/rubenv/sql-migrate v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/sigstore/fulcio v1.4.5 // indirect github.com/sigstore/rekor v1.3.6 // indirect github.com/sigstore/sigstore v1.8.4 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect diff --git a/go.sum b/go.sum index a5f224fb81..b991c83981 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= @@ -33,11 +33,10 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -180,8 +179,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= -github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= +github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= @@ -191,8 +190,8 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= -github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= +github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= +github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -245,7 +244,6 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -253,12 +251,6 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= -github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= -github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= -github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= -github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= -github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -323,7 +315,6 @@ github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAx github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -386,10 +377,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.1-0.20180514194441-a1dbeea552b7/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -399,8 +388,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joelanford/ignore v0.1.0 h1:VawbTDeg5EL+PN7W8gxVzGerfGpVo3gFdR5ZAqnkYRk= github.com/joelanford/ignore v0.1.0/go.mod h1:Vb0PQMAQXK29fmiPjDukpO8I2NTcp1y8LbhFijD1/0o= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -420,8 +409,6 @@ github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 h1:4bcRTTSx+LKSxM github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368/go.mod h1:lKGj1op99m4GtQISxoD2t+K+WO/q2NzEPKvfXFQfbCA= github.com/k14s/ytt v0.36.0 h1:ERr7q+r3ziYJv91fvTx2b76d1MIo3SI/EsAS01WU+Zo= github.com/k14s/ytt v0.36.0/go.mod h1:awQ3bHBk1qT2Xn3GJVdmaLss2khZOIBBKFd2TNXZNMk= -github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= -github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -448,7 +435,6 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/letsencrypt/boulder v0.0.0-20240418210053-89b07f4543e0 h1:aiPrFdHDCCvigNBCkOWj2lv9Bx5xDp210OANZEoiP0I= github.com/letsencrypt/boulder v0.0.0-20240418210053-89b07f4543e0/go.mod h1:srVwm2N3DC/tWqQ+igZXDrmKlNRN8X/dmJ1wEZrv760= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= @@ -456,12 +442,6 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= -github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= -github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= -github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= -github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -474,7 +454,7 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0= github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -484,7 +464,6 @@ github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPk github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -499,7 +478,6 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -626,8 +604,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0= -github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= +github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI= +github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -638,9 +616,8 @@ github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbm github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sigstore/fulcio v1.4.5 h1:WWNnrOknD0DbruuZWCbN+86WRROpEl3Xts+WT2Ek1yc= github.com/sigstore/fulcio v1.4.5/go.mod h1:oz3Qwlma8dWcSS/IENR/6SjbW4ipN0cxpRVfgdsjMU8= @@ -657,9 +634,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -680,7 +656,6 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -720,7 +695,6 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= @@ -791,8 +765,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -817,7 +789,6 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -841,9 +812,6 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -858,7 +826,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -885,30 +852,21 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -936,7 +894,6 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1027,8 +984,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -helm.sh/helm/v3 v3.15.4 h1:UFHd6oZ1IN3FsUZ7XNhOQDyQ2QYknBNWRHH57e9cbHY= -helm.sh/helm/v3 v3.15.4/go.mod h1:phOwlxqGSgppCY/ysWBNRhG3MtnpsttOzxaTK+Mt40E= +helm.sh/helm/v3 v3.16.1 h1:cER6tI/8PgUAsaJaQCVBUg3VI9KN4oVaZJgY60RIc0c= +helm.sh/helm/v3 v3.16.1/go.mod h1:r+xBHHP20qJeEqtvBXMf7W35QDJnzY/eiEBzt+TfHps= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 188858cbf3062ecf1e9dfcb37c3dc866a138db77 Mon Sep 17 00:00:00 2001 From: Daniel Franz Date: Fri, 13 Sep 2024 07:45:59 -0700 Subject: [PATCH 009/694] Clean up catalogd API Ref generation (#1266) Also simplifies the verify github action. Signed-off-by: dtfranz --- .github/workflows/sanity.yaml | 27 +++++++-------------------- .gitignore | 3 ++- Makefile | 9 ++++++--- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml index 1c34818cd0..5b7a641a36 100644 --- a/.github/workflows/sanity.yaml +++ b/.github/workflows/sanity.yaml @@ -12,26 +12,13 @@ jobs: verify: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - with: - path: operator-controller - - uses: actions/setup-go@v5 - with: - go-version-file: "operator-controller/go.mod" - - name: Get catalogd version - id: get-catalogd-version - run: | - cd operator-controller - echo "CATALOGD_VERSION=$(go list -mod=mod -m -f "{{.Version}}" github.com/operator-framework/catalogd)" >> "$GITHUB_OUTPUT" - - uses: actions/checkout@v4 - with: - repository: operator-framework/catalogd - path: catalogd - ref: "${{ steps.get-catalogd-version.outputs.CATALOGD_VERSION }}" - - name: Run verification checks - run: | - cd operator-controller - make verify + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + - name: Run verification checks + run: make verify lint: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index d7ffdb1b3d..305a463ba6 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,5 @@ install.sh site .tiltbuild/ -.vscode \ No newline at end of file +.catalogd-tmp/ +.vscode diff --git a/Makefile b/Makefile index afca4ce824..2b07b3ab27 100644 --- a/Makefile +++ b/Makefile @@ -311,16 +311,19 @@ quickstart: $(KUSTOMIZE) manifests #EXHELP Generate the installation release man .PHONY: crd-ref-docs OPERATOR_CONTROLLER_API_REFERENCE_FILENAME := operator-controller-api-reference.md CATALOGD_API_REFERENCE_FILENAME := catalogd-api-reference.md -CATALOGD_PATH := "$(ROOT_DIR)/../catalogd" -crd-ref-docs: $(CRD_REF_DOCS) #EXHELP Generate the API Reference Documents. NOTE: catalogd must be cloned into operator-controller's parent directory. +CATALOGD_TMP_DIR := $(ROOT_DIR)/.catalogd-tmp/ +crd-ref-docs: $(CRD_REF_DOCS) #EXHELP Generate the API Reference Documents. rm -f $(ROOT_DIR)/docs/refs/api/$(OPERATOR_CONTROLLER_API_REFERENCE_FILENAME) $(CRD_REF_DOCS) --source-path=$(ROOT_DIR)/api \ --config=$(ROOT_DIR)/docs/refs/api/crd-ref-docs-gen-config.yaml \ --renderer=markdown --output-path=$(ROOT_DIR)/docs/refs/api/$(OPERATOR_CONTROLLER_API_REFERENCE_FILENAME); + rm -rf $(CATALOGD_TMP_DIR) + git clone --depth 1 --branch $(CATALOGD_VERSION) https://github.com/operator-framework/catalogd $(CATALOGD_TMP_DIR) rm -f $(ROOT_DIR)/docs/refs/api/$(CATALOGD_API_REFERENCE_FILENAME) - $(CRD_REF_DOCS) --source-path=$(CATALOGD_PATH)/api \ + $(CRD_REF_DOCS) --source-path=$(CATALOGD_TMP_DIR)/api \ --config=$(ROOT_DIR)/docs/refs/api/crd-ref-docs-gen-config.yaml \ --renderer=markdown --output-path=$(ROOT_DIR)/docs/refs/api/$(CATALOGD_API_REFERENCE_FILENAME) + rm -rf $(CATALOGD_TMP_DIR)/ VENVDIR := $(abspath docs/.venv) From f10eb1d25971679141eef46156b7c6a6a0e2b0ab Mon Sep 17 00:00:00 2001 From: Todd Short Date: Mon, 16 Sep 2024 10:26:00 -0400 Subject: [PATCH 010/694] Add CI to check if golang version updated (#1264) Signed-off-by: Todd Short --- .github/workflows/go-verdiff.yaml | 16 +++++++ Makefile | 4 +- hack/tools/check-go-version.sh | 72 +++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/go-verdiff.yaml create mode 100755 hack/tools/check-go-version.sh diff --git a/.github/workflows/go-verdiff.yaml b/.github/workflows/go-verdiff.yaml new file mode 100644 index 0000000000..4008a7cecd --- /dev/null +++ b/.github/workflows/go-verdiff.yaml @@ -0,0 +1,16 @@ +name: go-verdiff +on: + pull_request: + paths: + - '**.mod' + branches: + - main +jobs: + go-verdiff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Check golang version + run: hack/tools/check-go-version.sh "${{ github.event.pull_request.base.sha }}" diff --git a/Makefile b/Makefile index 2b07b3ab27..ffc8b7b6a6 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ SHELL := /usr/bin/env bash -o pipefail .SHELLFLAGS := -ec export ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +GOLANG_VERSION := $(shell sed -En 's/^go (.*)$$/\1/p' "go.mod") # Image URL to use all building/pushing image targets ifeq ($(origin IMAGE_REPO), undefined) IMAGE_REPO := quay.io/operator-framework/operator-controller @@ -95,7 +96,8 @@ lint: $(GOLANGCI_LINT) #HELP Run golangci linter. .PHONY: tidy tidy: #HELP Update dependencies. - $(Q)go mod tidy + # Force tidy to use the version already in go.mod + $(Q)go mod tidy -go=$(GOLANG_VERSION) .PHONY: manifests manifests: $(CONTROLLER_GEN) #EXHELP Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. diff --git a/hack/tools/check-go-version.sh b/hack/tools/check-go-version.sh new file mode 100755 index 0000000000..7de77b60d7 --- /dev/null +++ b/hack/tools/check-go-version.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +BASE_REF=${1:-main} +GO_VER=$(sed -En 's/^go (.*)$/\1/p' "go.mod") +OLDIFS="${IFS}" +IFS='.' MAX_VER=(${GO_VER}) +IFS="${OLDIFS}" + +if [ ${#MAX_VER[*]} -ne 3 -a ${#MAX_VER[*]} -ne 2 ]; then + echo "Invalid go version: ${GO_VER}" + exit 1 +fi + +GO_MAJOR=${MAX_VER[0]} +GO_MINOR=${MAX_VER[1]} +GO_PATCH=${MAX_VER[2]} + +RETCODE=0 + +check_version () { + local whole=$1 + local file=$2 + OLDIFS="${IFS}" + IFS='.' ver=(${whole}) + IFS="${OLDIFS}" + + if [ ${ver[0]} -gt ${GO_MAJOR} ]; then + echo "${file}: ${whole}: Bad golang version (expected ${GO_VER} or less)" + return 1 + fi + if [ ${ver[1]} -gt ${GO_MINOR} ]; then + echo "${file}: ${whole}: Bad golang version (expected ${GO_VER} or less)" + return 1 + fi + + if [ ${#ver[*]} -eq 2 ] ; then + return 0 + fi + if [ ${#ver[*]} -ne 3 ] ; then + echo "${file}: ${whole}: Badly formatted golang version" + return 1 + fi + + if [ ${ver[1]} -eq ${GO_MINOR} -a ${ver[2]} -gt ${GO_PATCH} ]; then + echo "${file}: ${whole}: Bad golang version (expected ${GO_VER} or less)" + return 1 + fi + return 0 +} + +echo "Found golang version: ${GO_VER}" + +for f in $(find . -name "*.mod"); do + v=$(sed -En 's/^go (.*)$/\1/p' ${f}) + if [ -z ${v} ]; then + echo "${f}: Skipping, no version found" + continue + fi + if ! check_version ${v} ${f}; then + RETCODE=1 + fi + old=$(git grep -ohP '^go .*$' "${BASE_REF}" -- "${f}") + old=${old#go } + new=$(git grep -ohP '^go .*$' "${f}") + new=${new#go } + if [ "${new}" != "${old}" ]; then + echo "${f}: ${v}: Updated golang version from ${old}" + RETCODE=1 + fi +done + +exit ${RETCODE} From 0bcc7f85fa23d90cfc3e5a5c64f19799d7acb8fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:57:20 +0200 Subject: [PATCH 011/694] :seedling: Bump platformdirs from 4.3.2 to 4.3.3 (#1272) Bumps [platformdirs](https://github.com/tox-dev/platformdirs) from 4.3.2 to 4.3.3. - [Release notes](https://github.com/tox-dev/platformdirs/releases) - [Changelog](https://github.com/tox-dev/platformdirs/blob/main/CHANGES.rst) - [Commits](https://github.com/tox-dev/platformdirs/compare/4.3.2...4.3.3) --- updated-dependencies: - dependency-name: platformdirs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1189e78710..7bf94e6e96 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 pathspec==0.12.1 -platformdirs==4.3.2 +platformdirs==4.3.3 Pygments==2.18.0 pymdown-extensions==10.9 pyquery==2.0.1 From 6a53f564648d413943338c643682af621d9778e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:57:35 +0200 Subject: [PATCH 012/694] :seedling: Bump idna from 3.8 to 3.10 (#1273) Bumps [idna](https://github.com/kjd/idna) from 3.8 to 3.10. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst) - [Commits](https://github.com/kjd/idna/compare/v3.8...v3.10) --- updated-dependencies: - dependency-name: idna dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7bf94e6e96..e48d17d152 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ click==8.1.7 colorama==0.4.6 cssselect==1.2.0 ghp-import==2.1.0 -idna==3.8 +idna==3.10 Jinja2==3.1.4 lxml==5.3.0 Markdown==3.7 From e9584dbbef2a4545b724ab9ffd3ef87a8d169547 Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Mon, 16 Sep 2024 18:03:12 +0200 Subject: [PATCH 013/694] :seedling: Add catalog exploration and extension rbac/installation scripts (#1216) * Add support libraries Signed-off-by: Per Goncalves da Silva * Add download-catalog script Signed-off-by: Per Goncalves da Silva * Add find-bundle-image script Signed-off-by: Per Goncalves da Silva * Add is-bundle-supported script Signed-off-by: Per Goncalves da Silva * Add list-compatible-bundles script Signed-off-by: Per Goncalves da Silva * Add unpack-bundle script Signed-off-by: Per Goncalves da Silva * Add generate-manifests script Signed-off-by: Per Goncalves da Silva * Address reviewer comments Signed-off-by: Per Goncalves da Silva --------- Signed-off-by: Per Goncalves da Silva Co-authored-by: Per Goncalves da Silva --- hack/tools/catalogs/README.md | 177 ++++++++++++++++++ hack/tools/catalogs/download-catalog | 95 ++++++++++ hack/tools/catalogs/find-bundle-image | 33 ++++ hack/tools/catalogs/generate-manifests | 182 ++++++++++++++++++ hack/tools/catalogs/is-bundle-supported | 41 +++++ hack/tools/catalogs/lib/bundle.sh | 194 ++++++++++++++++++++ hack/tools/catalogs/lib/collect-rbac.sh | 144 +++++++++++++++ hack/tools/catalogs/lib/hash.sh | 63 +++++++ hack/tools/catalogs/lib/manifests.sh | 121 ++++++++++++ hack/tools/catalogs/lib/rbac.sh | 115 ++++++++++++ hack/tools/catalogs/lib/unpack.sh | 38 ++++ hack/tools/catalogs/lib/utils.sh | 49 +++++ hack/tools/catalogs/list-compatible-bundles | 96 ++++++++++ hack/tools/catalogs/unpack-bundle | 73 ++++++++ 14 files changed, 1421 insertions(+) create mode 100644 hack/tools/catalogs/README.md create mode 100755 hack/tools/catalogs/download-catalog create mode 100755 hack/tools/catalogs/find-bundle-image create mode 100755 hack/tools/catalogs/generate-manifests create mode 100755 hack/tools/catalogs/is-bundle-supported create mode 100644 hack/tools/catalogs/lib/bundle.sh create mode 100644 hack/tools/catalogs/lib/collect-rbac.sh create mode 100644 hack/tools/catalogs/lib/hash.sh create mode 100644 hack/tools/catalogs/lib/manifests.sh create mode 100644 hack/tools/catalogs/lib/rbac.sh create mode 100644 hack/tools/catalogs/lib/unpack.sh create mode 100644 hack/tools/catalogs/lib/utils.sh create mode 100755 hack/tools/catalogs/list-compatible-bundles create mode 100755 hack/tools/catalogs/unpack-bundle diff --git a/hack/tools/catalogs/README.md b/hack/tools/catalogs/README.md new file mode 100644 index 0000000000..00360a17e6 --- /dev/null +++ b/hack/tools/catalogs/README.md @@ -0,0 +1,177 @@ +# Hack Catalog Tools + +This directory contains scripts that automate some of the tasks related to catalog interaction and bundle installation. + +--- +> [!WARNING] +> These scripts are intended to help users navigate the catalog and produce installation RBAC until reliable tooling is available for OLM v1, +> and to document the process in code for contributors. These scripts are not officially supported. +> They are not meant to be used in production environments. +--- + +### Prerequisites + +To execute the scripts, the following tools are required: + + * [jq](https://jqlang.github.io/jq/) to filter catalog data + * [yq](https://mikefarah.gitbook.io/yq/) to parse YAML + * [kubectl](https://kubernetes.io/docs/reference/kubectl/) to interact with the cluster running OLM v1 + * [wget](https://www.gnu.org/software/wget/) to download the catalog data + * A container runtime, such as [podman](https://podman.io/) or [docker](https://www.docker.com/) to interact with bundle images. + +#### Container Runtime + +By default, the scripts use `podman` or `docker` as the container runtime. +If you use another container runtime, set the `CONTAINER_RUNTIME` environment variable to the path of the container runtime binary. + +### Tools + +--- +> [!NOTE] +> All examples assume that the current working directory is the `hack/tools/catalogs` directory. +--- + +#### download-catalog + +Download a catalog from an unpacked ClusterCatalog running on a cluster reachable by `kubectl`. + +Example: + + ```terminal + # Download the catalog from the operatorhubio ClusterCatalog + ./download-catalog operatorhubio + ``` + +The downloaded catalog is saved to -catalog.json in the current directory. + +#### list-compatible-bundles + +List (potential) OLM v1 compatible bundles from the catalog. + +Not all registry+v1 bundles made for OLM v0 are compatible with OLM v1. +Compatible bundles must meet the following criteria: + * Support for the 'AllNamespaces' install mode + * No webhooks + * No dependencies on other packages of GVKs + * The operator does not make use of OLM v0's [`OperatorCondition`](https://olm.operatorframework.io/docs/concepts/crds/operatorcondition/) API + + +For more information, see [OLM v1 limitations](../../../docs/refs/olm-v1-limitations.md). + +For some bundles, some of this criteria can only be determined by inspecting the contents bundle image. The script will return all bundles that are potentially compatible. + +Examples: + + ``` terminal + # List (potentially) OLM v1 compatible bundles from the operatorhubio catalog + ./list-compatible-bundles < operatorhubio-catalog.json + ``` + + ``` terminal + # List (potentially) OLM v1 compatible bundles that contain 'argco' in the package name + # -r can be used with any regex supported by jq + ./list-compatible-bundles -r 'argocd' < operatorhubio-catalog.json + ``` + +#### find-bundle-image + +Find the image for a bundle in the catalog. + +Example: + + ``` terminal + # Get the image for the argocd-operator v0.6.0 bundle from the operatorhubio catalog + ./find-bundle-image argocd-operator 0.6.0 < operatorhubio-catalog.json + ``` + +#### unpack-bundle + +Unpack a bundle image to a directory. + +Example: + + ``` terminal + # Unpack the argocd-operator v0.6.0 bundle image to a temporary directory + ./unpack-bundle quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3 + ``` + + ``` terminal + # Unpack the argocd-operator v0.6.0 bundle image to a specific directory + ./unpack-bundle quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3 -o argocd-manifests + ``` + +#### is-bundle-supported + +Check if a bundle is supported by OLM v1 by inspecting the unpacked bundle manifests. + + +For more information on bundle support, see [OLM v1 limitations](../../../docs/refs/olm-v1-limitations.md). + +Example: + + ``` terminal + # Check if the argocd-operator v0.6.0 bundle from the operatorhubio catalog is supported by OLM v1 + ./is-bundle-supported argocd-manifests + ``` + + ``` terminal + # Find bundle image, unpack, and verify support in one command + ./find-bundle-image argocd-operator 0.6.0 < operatorhubio-catalog.json | ./unpack-bundle | ./is-bundle-supported + ``` + +#### generate-manifests + +Generate RBAC or installation manifests for a bundle. The generated manifests can be templates or fully rendered manifests. + +The following options can be used to override resource naming defaults: + -n Namespace where the extension is installed + -e - Name of the extension + -cr - Name of the cluster role + -r - Name of the role + -s - Name of the service account + --template - Generate template manifests + +Default resource name format: + * Namespace: -system + * Extension name: + * ClusterRole name: -cluster-role + * Role name: -installer-role + * ServiceAccount name: -installer + * ClusterRoleBinding name: -binding + * RoleBinding name: -binding + +Use `--template` to generate templated manifests that can be customized before applying to the cluster. +Template manifests will contain the following template variables: + +Template Variables: +* `${NAMESPACE}` - Namespace where the extension is installed +* `${EXTENSION_NAME}` - Name of the extension +* `${CLUSTER_ROLE_NAME}` - Name of the cluster role +* `${ROLE_NAME}` - Name of the role +* `${SERVICE_ACCOUNT_NAME}` - Name of the service account + +Examples: + + ``` terminal + # Generate installation manifests for the argocd-operator v0.6.0 bundle from the operatorhubio catalog + ./generate-manifests install argocd-operator 0.6.0 < operatorhubio-catalog.json + ``` + + ``` terminal + # Generate templated installation manifests for the argocd-operator v0.6.0 bundle from the operatorhubio catalog + generate-manifests install argocd-operator 0.6.0 --template < operatorhubio-catalog.json + ``` + + ``` terminal + # Generate RBAC manifests for the argocd-operator v0.6.0 bundle from the operatorhubio catalog + generate-manifests rbac argocd-operator 0.6.0 < operatorhubio-catalog.json + ``` + + ``` terminal + # Generate templated RBAC manifests for the argocd-operator v0.6.0 bundle from the operatorhubio catalog + generate-manifests rbac argocd-operator 0.6.0 --template < operatorhubio-catalog.json + ``` diff --git a/hack/tools/catalogs/download-catalog b/hack/tools/catalogs/download-catalog new file mode 100755 index 0000000000..cfb7e51a9a --- /dev/null +++ b/hack/tools/catalogs/download-catalog @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +SCRIPT_ROOT=$(dirname "$(realpath "$0")") +source "${SCRIPT_ROOT}/lib/utils.sh" + +# Check required tools are installed +assert-commands kubectl jq wget + +# ClusterCatalog coordinates +: "${CATALOGD_CATALOGD_SERVICE_NAMESPACE:=olmv1-system}" +: "${CATALOGD_SERVICE_NAME:=catalogd-catalogserver}" +: "${CATALOGD_SERVICE_PORT:=443}" # Assumes the service uses HTTPS on port 443 +: "${CATALOGD_LOCAL_SERVICE_PORT:=8001}" + +echo "Namespace: $CATALOGD_CATALOGD_SERVICE_NAMESPACE" +echo "Service Name: $CATALOGD_SERVICE_NAME" +echo "Service Port: $CATALOGD_SERVICE_PORT" +echo "Local Service Port: $CATALOGD_LOCAL_SERVICE_PORT" + +# Display usage +usage() { + print-banner + echo "" + echo "Usage: $0 " + echo "" + echo "Download catalog from a ClusterCatalog in a cluster reachable from KUBECONFIG" + echo "Downloaded catalog will be saved as -catalog.json" + echo "" + echo "Example:" + echo " $0 operatorhubio" +} + +# Check if catalog name is provided +if [ -z "$1" ]; then + usage + exit 1 +fi + +CATALOG_NAME="$1" + +# Check if the clustercatalog resource exists +echo "Checking if ClusterCatalog $CATALOG_NAME exists..." +CLUSTER_CATALOG=$(kubectl get clustercatalog "$CATALOG_NAME" -o json 2>/dev/null) +if [ -z "$CLUSTER_CATALOG" ]; then + echo "ClusterCatalog $CATALOG_NAME does not exist." + exit 1 +fi + +# Check if the Unpacked condition is true +UNPACKED_CONDITION=$(echo "$CLUSTER_CATALOG" | jq -r '.status.conditions[]? // [] | select(.type=="Unpacked") | .status') +if [ "$UNPACKED_CONDITION" != "True" ]; then + echo "ClusterCatalog $CATALOG_NAME is not unpacked yet." + exit 1 +fi + +# Get the contentURL +CONTENT_URL=$(echo "$CLUSTER_CATALOG" | jq -r '.status.contentURL') +if [ -z "$CONTENT_URL" ]; then + echo "Content URL not found for ClusterCatalog $CATALOG_NAME." + exit 1 +fi + +# Start port forwarding +echo "Starting kubectl port-forward to $CATALOGD_SERVICE_NAME on port $CATALOGD_LOCAL_SERVICE_PORT..." +kubectl port-forward -n "$CATALOGD_CATALOGD_SERVICE_NAMESPACE" svc/"$CATALOGD_SERVICE_NAME" "$CATALOGD_LOCAL_SERVICE_PORT:$CATALOGD_SERVICE_PORT" &>/dev/null & +PORT_FORWARD_PID=$! + +# Poll the service until it responds or timeout after 30 seconds +timeout=30 +while ! curl -s "http://localhost:${CATALOGD_LOCAL_SERVICE_PORT}" >/dev/null; do + timeout=$((timeout - 1)) + if [ $timeout -le 0 ]; then + echo "Port forwarding failed to start within 30 seconds." + kill $PORT_FORWARD_PID + exit 1 + fi + sleep 1 +done + +# Modify the contentURL to hit localhost: +LOCAL_CONTENT_URL=${CONTENT_URL//https:\/\/$CATALOGD_SERVICE_NAME.$CATALOGD_CATALOGD_SERVICE_NAMESPACE.svc/https:\/\/localhost:$CATALOGD_LOCAL_SERVICE_PORT} +echo "Found content URL: $CONTENT_URL" +echo "Using local port: $CATALOGD_LOCAL_SERVICE_PORT" +echo "Using local content URL: $LOCAL_CONTENT_URL" + +# shellcheck disable=SC2001 +# Download the catalog using wget +echo "Downloading catalog from $LOCAL_CONTENT_URL..." +wget --no-check-certificate "$LOCAL_CONTENT_URL" -O "${CATALOG_NAME}-catalog.json" + +# Stop the port forwarding +echo "Stopping kubectl port-forward..." +kill $PORT_FORWARD_PID + +echo "Catalog downloaded to ${CATALOG_NAME}-catalog.json" \ No newline at end of file diff --git a/hack/tools/catalogs/find-bundle-image b/hack/tools/catalogs/find-bundle-image new file mode 100755 index 0000000000..9394e6b72e --- /dev/null +++ b/hack/tools/catalogs/find-bundle-image @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# Get the directory of the current script +SCRIPT_ROOT=$(dirname "$(realpath "$0")") + +source "${SCRIPT_ROOT}/lib/bundle.sh" +source "${SCRIPT_ROOT}/lib/utils.sh" + +# Check required tools are installed +assert-commands jq + +usage() { + print-banner + echo "" + echo "Usage: $0 " + echo "" + echo "Find the bundle image for a package in a catalog in " + echo "" + echo "Example:" + echo " $0 argocd-operator 0.6.0 < operatorhubio-catalog.json" +} + +if [ "$#" -lt 2 ]; then + usage + exit 1 +fi + +package_name="$1" +package_version="$2" + +# Find bundle image +image="$(cat - | get-bundle-image "${package_name}" "${package_version}")" +echo "${image}" diff --git a/hack/tools/catalogs/generate-manifests b/hack/tools/catalogs/generate-manifests new file mode 100755 index 0000000000..ee1ee97b04 --- /dev/null +++ b/hack/tools/catalogs/generate-manifests @@ -0,0 +1,182 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +# Get the directory of the current script +SCRIPT_ROOT=$(dirname "$(realpath "$0")") + +source "${SCRIPT_ROOT}/lib/unpack.sh" +source "${SCRIPT_ROOT}/lib/collect-rbac.sh" +source "${SCRIPT_ROOT}/lib/utils.sh" + +# Check there is a container runtime (podman, or docker) +# If neither are found, check CONTAINER_RUNTIME is set and exists in PATH +assert-container-runtime + +# Check required tools are installed +assert-commands jq + +function usage() { + print-banner + echo "" + echo "Usage:" + echo "" + echo "Generate installation manifests" + echo "$0 install [-n namespace] [-e cluster-extension-name] [-cr cluster-role-name] [-r role-name] [-s service-account-name] [--template]" + echo "" + echo "Generate RBAC manifests" + echo "$0 rbac [-n namespace] [-e cluster-extension-name] [-cr cluster-role-name] [-r role-name] [-s service-account-name] [--template]" + echo "" + echo "Generate installation or RBAC manifests for a registry+v1 package given an FBC catalog in stdin" + echo "" + echo "Options:" + echo " -n - Namespace where the extension is installed" + echo " -e - Name of the extension" + echo " -cr - Name of the cluster role" + echo " -r - Name of the role" + echo " -s - Name of the service account" + echo " --template - Generate template manifests" + echo "" + echo "Template Variables:" + echo " * \${NAMESPACE} - Namespace where the extension is installed" + echo " * \${EXTENSION_NAME} - Name of the extension" + echo " * \${CLUSTER_ROLE_NAME} - Name of the cluster role" + echo " * \${ROLE_NAME} - Name of the role" + echo " * \${SERVICE_ACCOUNT_NAME} - Name of the service account" + echo "" + echo "Default Resource Name Format:" + echo " * Namespace: -system" + echo " * Extension name: " + echo " * ClusterRole name: -cluster-role" + echo " * Role name: -installer-role" + echo " * ServiceAccount name: -installer" + echo " * ClusterRoleBinding name: -binding" + echo " * RoleBinding name: -binding" + echo "" + echo "Examples:" + echo " # Generate installation manifests for the argocd-operator package" + echo " $0 install argocd-operator 0.6.0 < operatorhubio-catalog.json" + echo "" + echo " # Generate RBAC manifests for the argocd-operator package" + echo " $0 rbac argocd-operator 0.6.0 < operatorhubio-catalog.json" + echo "" + echo " # Generate templated installation manifests for the argocd-operator package" + echo " $0 install argocd-operator 0.6.0 --template < operatorhubio-catalog.json" + echo "" + echo " # Generate templated RBAC manifests for the argocd-operator package" + echo " $0 rbac argocd-operator 0.6.0 --template < operatorhubio-catalog.json" + echo "" + echo "WARNING: This script is a stopgap solution until proper tools are available in OLMv1 - it is not guaranteed to work with all packages." +} + +# Check for at least 3 arguments +if [ "$#" -lt 3 ]; then + usage + exit 1 +fi + +# Command and package details +COMMAND=$1 +export PACKAGE_NAME=$2 +export PACKAGE_VERSION=$3 + +# Initialize environment variables with template defaults +export NAMESPACE="\${NAMESPACE}" +export EXTENSION_NAME="\${EXTENSION_NAME}" +export CLUSTER_ROLE_NAME="\${CLUSTER_ROLE_NAME}" +export ROLE_NAME="\${ROLE_NAME}" +export SERVICE_ACCOUNT_NAME="\${SERVICE_ACCOUNT_NAME}" +export DEBUG=false +template=false + +# Parse optional arguments +shift 3 +while [[ $# -gt 0 ]]; do + key="$1" + + case $key in + -n) + export NAMESPACE="$2" + shift 2 + ;; + -e) + export EXTENSION_NAME="$2" + shift 2 + ;; + -cr) + export CLUSTER_ROLE_NAME="$2" + shift 2 + ;; + -r) + export ROLE_NAME="$2" + shift 2 + ;; + -s) + export SERVICE_ACCOUNT_NAME="$2" + shift 2 + ;; + --template) + template=true + shift + ;; + --debug) + DEBUG=1 + shift + ;; + *) + echo "Unknown option $1" + usage + exit 1 + ;; + esac +done + +# Apply default values only to unset parameters if --template is not set +if [ "$template" = false ]; then + [ "$EXTENSION_NAME" == "\${EXTENSION_NAME}" ] && export EXTENSION_NAME="${PACKAGE_NAME}" + [ "$NAMESPACE" == "\${NAMESPACE}" ] && export NAMESPACE="${EXTENSION_NAME}-system" + [ "$SERVICE_ACCOUNT_NAME" == "\${SERVICE_ACCOUNT_NAME}" ] && export SERVICE_ACCOUNT_NAME="${PACKAGE_NAME}-installer" + [ "$CLUSTER_ROLE_NAME" == "\${CLUSTER_ROLE_NAME}" ] && export CLUSTER_ROLE_NAME="${SERVICE_ACCOUNT_NAME}-cluster-role" + [ "$ROLE_NAME" == "\${ROLE_NAME}" ] && export ROLE_NAME="${SERVICE_ACCOUNT_NAME}-installer-role" +fi + +# Output the set environment variables for confirmation +debug "Environment variables set:" +debug "NAMESPACE=${NAMESPACE}" +debug "EXTENSION_NAME=${EXTENSION_NAME}" +debug "CLUSTER_ROLE_NAME=${CLUSTER_ROLE_NAME}" +debug "ROLE_NAME=${ROLE_NAME}" +debug "SERVICE_ACCOUNT_NAME=${SERVICE_ACCOUNT_NAME}" + +# Find bundle image +image="$(cat - | get-bundle-image "${PACKAGE_NAME}" "${PACKAGE_VERSION}")" + +# Unpack and close container +bundle_manifest_dir="$("${SCRIPT_ROOT}/unpack-bundle" "${image}")" + +# Derive rbac from bundle manifests +collect_installer_rbac "${bundle_manifest_dir}" +echo "Done" >&2 + +# Example output or further processing based on command +case "${COMMAND}" in + install) + generate_install_manifests | envsubst + ;; + rbac) + generate_rbac_manifests | envsubst + ;; + *) + echo "Unknown command ${COMMAND}" + usage + exit 1 + ;; +esac + +# Clean up manifest directory +if [ "${DEBUG,,}" != "false" ]; then + debug "Skipping cleanup of manifest directory: ${bundle_manifest_dir}" +else + rm -rf "${bundle_manifest_dir}" +fi \ No newline at end of file diff --git a/hack/tools/catalogs/is-bundle-supported b/hack/tools/catalogs/is-bundle-supported new file mode 100755 index 0000000000..b197b53102 --- /dev/null +++ b/hack/tools/catalogs/is-bundle-supported @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# Get the directory of the current script +SCRIPT_ROOT=$(dirname "$(realpath "$0")") + +source "${SCRIPT_ROOT}/lib/unpack.sh" +source "${SCRIPT_ROOT}/lib/collect-rbac.sh" +source "${SCRIPT_ROOT}/lib/utils.sh" + +# Check required tools are installed +assert-commands jq + +usage() { + print-banner + echo "" + echo "Usage: $0 " + echo "" + echo "Check if a bundle is supported by OLM v1 given a bundle manifest directory" + echo "" + echo "Example:" + echo " $0 " +} + +# Must have a single argument +if [ -z "$1" ]; then + if [ -t 0 ]; then + echo "Error: Docker image name is required." + usage + exit 1 + else + read -r manifest_dir + fi +else + manifest_dir="$1" +fi + + # Check the bundle is supported +assert-bundle-supported "${manifest_dir}" + +echo "Bundle is supported by OLM v1" >&2 +echo "true" \ No newline at end of file diff --git a/hack/tools/catalogs/lib/bundle.sh b/hack/tools/catalogs/lib/bundle.sh new file mode 100644 index 0000000000..2adfc260f0 --- /dev/null +++ b/hack/tools/catalogs/lib/bundle.sh @@ -0,0 +1,194 @@ +# Library of functions for interacting with bundles and their manifests + +# SCRIPT_ROOT is the root of the script +source "$(dirname "${BASH_SOURCE[0]}")/hash.sh" + +# Given package and version grabs the bundle image from stdin FBC stream +get-bundle-image(){ + local package_name="${1}" + local package_version="${2}" + local image + image=$(jq -r --arg pkg "$package_name" --arg ver "$package_version" \ + 'select(.schema == "olm.bundle" and (.properties[] | select(.type == "olm.package" and .value.packageName == $pkg and .value.version == $ver))) | .image') + + if [ -z "$image" ]; then + echo "ERROR: No matching image found for package '$package_name' with version '$package_version'." >&2 + exit 1 + fi + + echo "${image}" +} + +is-all-namespace-mode-enabled() { + local csv="${1}" + local valid + # Note: The 'and true' is to ensure that the expression evaluates to true or false + # Without it, yq will return the matched value. This is done for succinctness. Without the 'and true', + # the same behavior could be achieved by checking if the output is non-empty. + valid=$(yq eval '(.spec.installModes[] | select(.type == "AllNamespaces" and .supported == true)) and true' "${csv}") + echo "${valid}" +} + +does-not-have-webhooks() { + local csv="${1}" + local valid + valid=$(yq eval '((.spec.webhookdefinitions == null) or (.spec.webhookdefinitions | length == 0))' "${csv}") + echo "${valid}" +} + +does-not-have-dependencies() { + local csv="${1}" + local valid + valid=$(yq eval '((.spec.customresourcedefinitions.required == null) or (.spec.customresourcedefinitions.required | length == 0))' "${csv}") + echo "${valid}" +} + +is-crd-version-supported() { + local manifest_dir="${1}" + local valid=true + while IFS= read -r resource_file; do + version=$(yq eval '.apiVersion' "$resource_file") + if [ "${version}" != "apiextensions.k8s.io/v1" ]; then + valid="${version}" + break + fi + done < <(find "${manifest_dir}" -type f -exec grep -l "^kind: CustomResourceDefinition" {} \;) + echo "$valid" +} + +is-bundle-supported() { + local manifest_dir="${1}" + csv="$(find_csv "${manifest_dir}")" + + crd_version="$(is-crd-version-supported "${manifest_dir}")" + + if [ "$(is-all-namespace-mode-enabled "${csv}")" != "true" ]; then + echo "Bundle not supported: AllNamespaces install mode is disabled" >&2 + echo "false" + elif [ "$(does-not-have-webhooks "${csv}")" != "true" ]; then + echo "Bundle not supported: contains webhooks" >&2 + echo "false" + elif [ "$(does-not-have-dependencies "${csv}")" != "true" ]; then + echo "Bundle not supported: contains dependencies" >&2 + echo "false" + elif [ "${crd_version}" != "true" ]; then + echo "Bundle not supported: unsupported CRD api version (${crd_version})" >&2 + echo "false" + fi + + echo "true" +} + +# Function to validate the bundle is supported +assert-bundle-supported() { + local manifest_dir="${1}" + if [ "$(is-bundle-supported "${manifest_dir}")" != "true" ]; then + exit 1 + fi +} + +# Function to get all resource names for a particular kind +# from the manifest directory +collect_resource_names() { + local manifest_dir="${1}" + local kind="${2}" + local resource_names=() + while IFS= read -r resource_file; do + name=$(yq eval -r '.metadata.name' "$resource_file") + if [ -n "$name" ]; then + resource_names+=("$name") + fi + done < <(find "${manifest_dir}" -type f -exec grep -l "^kind: ${kind}" {} \;) + echo "${resource_names[@]}" +} + +# Function that collects all the rules for all the ClusterRole manifests +# shipped with the bundle +collect_manifest_cluster_role_perms() { + local manifest_dir="${1}" + local kind="ClusterRole" + local all_cr_rules="[]" + + while IFS= read -r resource_file; do + # Extract the entire rules array from the current file and ensure it's treated as a valid JSON array + cr_rules=$(yq eval -o=json -r '.rules // []' "$resource_file") + # Validate and merge the current rules array with the cumulative rules array + if jq -e . >/dev/null 2>&1 <<<"$cr_rules"; then + all_cr_rules=$(jq -c --argjson existing "$all_cr_rules" --argjson new "$cr_rules" '$existing + $new' <<<"$all_cr_rules") + fi + done < <(find "${manifest_dir}" -type f -exec grep -l "^kind: ${kind}" {} \;) + echo "$all_cr_rules" +} + +# Function that collects all the rules for all the Role manifests +# shipped with the bundle +collect_manifest_role_perms() { + local manifest_dir="${1}" + local kind="Role" + local all_cr_rules="[]" + + while IFS= read -r resource_file; do + # Extract the entire rules array from the current file and ensure it's treated as a valid JSON array + cr_rules=$(yq eval -o=json -r '.rules // []' "$resource_file") + # Validate and merge the current rules array with the cumulative rules array + if jq -e . >/dev/null 2>&1 <<<"$cr_rules"; then + all_cr_rules=$(jq -c --argjson existing "$all_cr_rules" --argjson new "$cr_rules" '$existing + $new' <<<"$all_cr_rules") + fi + done < <(find "${manifest_dir}" -type f -exec grep -l "^kind: ${kind}" {} \;) + echo "$all_cr_rules" +} + +# Function to get the apiGroup for a named resource of a given kind +# from the manifests dir +get_api_group() { + local dir_path="$1" + local kind="$2" + local name="$3" + + # Find the file containing the specified kind and name + local file + file=$(grep -rl "kind: $kind" "$dir_path" | xargs grep -l "name: $name") + + # Extract the apiGroup from the found file + if [ -n "$file" ]; then + local api_group + api_group=$(yq eval '.apiVersion' "$file" | awk -F'/' '{print $1}') + echo "$api_group" + fi +} + +# Function to get the generated clusterrole resource names +generated_cluster_role_names() { + local csvFile="${1}" + local generated_cluster_role_names=() + csv_name=$(yq eval -r '.metadata.name' "${csvFile}") + cperms=$(yq eval -o=json -r '.spec.install.spec.clusterPermissions? // []' "$csvFile" | jq -c '.[] | {serviceAccountName, rules: [.rules[] | {verbs, apiGroups, resources, resourceNames, nonResourceURLs} | with_entries(select(.value != null and .value != []))]}') + rbacPerms=$(yq eval -o=json -r '.spec.install.spec.permissions? // []' "$csvFile" | jq -c '.[] | {serviceAccountName, rules: [.rules[] | {verbs, apiGroups, resources, resourceNames, nonResourceURLs} | with_entries(select(.value != null and .value != []))]}') + allPerms=("${cperms[@]}" "${rbacPerms[@]}") + for perm in "${allPerms[@]}"; do + sa=$(echo "$perm" | yq eval -r '.serviceAccountName') + generated_name="$(generate_name "${csv_name}-${sa}" "${perm}")" + generated_cluster_role_names+=("${generated_name}") + done + echo "${generated_cluster_role_names[@]}" +} + +# Get CSV from manifest directory +find_csv() { + local manifest_dir="${1}" + local csv_files + + # Use grep -l to find files containing "kind: ClusterServiceVersion" + csv_files=$(grep -l "kind: ClusterServiceVersion" "$manifest_dir"/*.yaml) + + # Check if multiple CSV files are found + if [ "$(echo "$csv_files" | wc -l)" -gt 1 ]; then + echo "Error: Multiple CSV files found in ${manifest_dir}." + return 1 + elif [ -z "$csv_files" ]; then + echo "Error: No CSV file found in ${manifest_dir}." + return 1 + else + echo "${csv_files}" + fi +} \ No newline at end of file diff --git a/hack/tools/catalogs/lib/collect-rbac.sh b/hack/tools/catalogs/lib/collect-rbac.sh new file mode 100644 index 0000000000..33ed54da3e --- /dev/null +++ b/hack/tools/catalogs/lib/collect-rbac.sh @@ -0,0 +1,144 @@ +# Library of functions for collecting RBAC from manifests for the purposes of generating +# The required RBAC for the cluster extension installation +source "$(dirname "${BASH_SOURCE[0]}")/utils.sh" +source "$(dirname "${BASH_SOURCE[0]}")/rbac.sh" +source "$(dirname "${BASH_SOURCE[0]}")/manifests.sh" +source "$(dirname "${BASH_SOURCE[0]}")/bundle.sh" + +# Function to add the specified rules +add_required_rules() { + local finalizer_perm + finalizer_perm=$(make_rbac_rule "olm.operatorframework.io" "clusterextensions/finalizers" '"update"' "$EXTENSION_NAME") + aggregate_rules "${finalizer_perm}" "cluster" +} + +collect_crd_rbac() { + debug "Collecting CRD permissions" + local csv="${1}" + crds=() + while IFS=$'\n' read -r crd; do + crds+=("$crd") + done < <(yq eval -o=json -r '.spec.customresourcedefinitions.owned[]?.name' "$csv") + add_rbac_rules "apiextensions.k8s.io" "customresourcedefinitions" "cluster" "${ALL_RESOURCE_VERBS}" "${NAMED_RESOURCE_VERBS}" "${crds[@]}" +} + +collect_cluster_role_rbac() { + local manifest_dir="${1}" + local csv="${2}" + debug "Adding ClusterRole RBAC" + + # Collect shipped ClusterRole names + # And the OLMv1 generated ClusterRole names + read -ra manifest_cluster_role_names <<< "$(collect_resource_names "${manifest_dir}" "ClusterRole")" + read -ra generated_cluster_role_names <<< "$(generated_cluster_role_names "${csv}")" + all_cluster_role_names=("${manifest_cluster_role_names[@]}" "${generated_cluster_role_names[@]}") + add_rbac_rules "rbac.authorization.k8s.io" "clusterroles" "cluster" "${ALL_RESOURCE_VERBS}" "${NAMED_RESOURCE_VERBS}" "${all_cluster_role_names[@]}" + + # Add all rules for defined in shipped ClusterRoles + # This allows the installer service account to grant rbac + manifest_cr_perms="$(collect_manifest_cluster_role_perms "${manifest_dir}")" + for item in $(echo "$manifest_cr_perms" | jq -c -r '.[]'); do + aggregate_rules "${item}" "cluster" + done + + debug "Adding ClusterPermissions" + # Add all cluster scoped rules for defined in the CSV + # This allows the installer service account to grant rbac + cluster_permissions=$(yq eval -o=json '.spec.install.spec.clusterPermissions[].rules?' "$csv" | jq -c '.[]') + for perm in ${cluster_permissions}; do + aggregate_rules "${perm}" "cluster" + done + + # Collect RBAC for cluster scoped manifest objects + collect_cluster_scoped_resource_rbac "${manifest_dir}" + + debug "Adding ClusterRoleBinding RBAC" + # Collect shipped ClusterRoleBinding names + # And the OLMv1 generated ClusterRoleBinding names (same as the generated ClusterRole names) + read -ra manifest_cluster_role_binding_names <<< "$(collect_resource_names "${manifest_dir}" "ClusterRoleBinding")" + all_cluster_role_binding_names=("${manifest_cluster_role_binding_names[@]}" "${generated_cluster_role_names[@]}") + add_rbac_rules "rbac.authorization.k8s.io" "clusterrolebindings" "cluster" "${ALL_RESOURCE_VERBS}" "${NAMED_RESOURCE_VERBS}" "${all_cluster_role_binding_names[@]}" +} + +collect_cluster_scoped_resource_rbac() { + debug "Adding other ClusterScoped Resources" + local manifest_dir="${1}" + for kind in "${CLUSTER_SCOPED_RESOURCES[@]}"; do + read -ra resource_names <<< "$(collect_resource_names "${manifest_dir}" "${kind}")" + if [ ${#resource_names[@]} -eq 0 ]; then + continue + fi + api_group=$(get_api_group "${manifest_dir}" "${kind}" "${resource_names[1]}") + add_rbac_rules "${api_group}" "${kind,,}s" "cluster" "${ALL_RESOURCE_VERBS}" "${NAMED_RESOURCE_VERBS}" "${resource_names[@]}" + done +} + +collect_operator_deployment_rbac() { + local manifest_dir="${1}" + local csv="${2}" + + debug "Adding Deployment RBAC" + read -ra manifest_dep_names <<< "$(collect_resource_names "${manifest_dir}" "Deployment")" + read -ra csv_deployments <<< "$(yq eval -o=json -r '.spec.install.spec.deployments[]?.name' "$csv")" + all_deployments=("${manifest_dep_names[@]}" "${csv_deployments[@]}") + add_rbac_rules "apps" "deployments" "namespace" "${ALL_RESOURCE_VERBS}" "${NAMED_RESOURCE_VERBS}" "${all_deployments[@]}" +} + +collect_service_account_rbac() { + debug "Adding ServiceAccount RBAC" + local manifest_dir="${1}" + local csv="${2}" + read -ra manifest_sas <<< "$(collect_resource_names "${manifest_dir}" "ServiceAccount")" + read -ra csv_sas <<< "$(yq eval '.. | select(has("serviceAccountName")) | .serviceAccountName' "${csv}" | sort -u)" + all_sas=("${manifest_sas[@]}" "${csv_sas[@]}") + add_rbac_rules "" "serviceaccounts" "namespace" "${ALL_RESOURCE_VERBS}" "${NAMED_RESOURCE_VERBS}" "${all_sas[@]}" +} + +collect_role_rbac() { + debug "Collecting Role RBAC" + local manifest_dir="${1}" + local csv="${2}" + + # Shipped Role manifest permissions + manifest_role_perms="$(collect_manifest_role_perms "${manifest_dir}")" + for item in $(echo "$manifest_role_perms" | jq -c -r '.[]'); do + aggregate_rules "${item}" "namespace" + done + + # CSV namespaced permissions + namespace_permissions=$(yq eval -o=json '.spec.install.spec.permissions[].rules?' "$csv" | jq -c '.[]') + for perm in ${namespace_permissions}; do + aggregate_rules "${perm}" "cluster" + done + + # Account for all other shipped namespace scoped resources + for kind in "${NAMESPACE_SCOPED_RESOURCES[@]}"; do + read -ra resource_names <<< "$(collect_resource_names "${manifest_dir}" "${kind}")" + if [ ${#resource_names[@]} -eq 0 ]; then + continue + fi + api_group=$(get_api_group "${manifest_dir}" "${kind}" "${resource_names[@]}") + add_rbac_rules "${api_group}" "${kind,,}s" "namespace" "${ALL_RESOURCE_VERBS}" "${NAMED_RESOURCE_VERBS}" "${resource_names[@]}" + done +} + +# Expects a supported bundle +collect_installer_rbac() { + local manifest_dir="${1}" + csv="$(find_csv "${manifest_dir}")" + + echo "Collecting RBAC from bundle (manifest_dir:${manifest_dir} csv: ${csv})" >&2 + + # Ensure bundle is supported by olmv1 + assert-bundle-supported "${manifest_dir}" + + # Add the required permissions rules + add_required_rules + + # Grab CSV name + collect_crd_rbac "${csv}" + collect_cluster_role_rbac "${manifest_dir}" "${csv}" + collect_operator_deployment_rbac "${manifest_dir}" "${csv}" + collect_service_account_rbac "${manifest_dir}" "${csv}" + collect_role_rbac "${manifest_dir}" "${csv}" +} diff --git a/hack/tools/catalogs/lib/hash.sh b/hack/tools/catalogs/lib/hash.sh new file mode 100644 index 0000000000..deccb7e8d5 --- /dev/null +++ b/hack/tools/catalogs/lib/hash.sh @@ -0,0 +1,63 @@ +# Library of functions that reproduce hashing behavior in internal/rukpak/convert/registryv1.go:generate_name() +# This is used to generate unique names for the ClusterRoles generated by O-C during bundle installation + +base36encode() { + if [[ -z "$1" || ! "$1" =~ ^[0-9a-fA-F]+$ ]]; then + echo "Invalid input. Please provide a hexadecimal number." + return 1 + fi + + # Convert hexadecimal to decimal + local bigint + bigint=$(echo "ibase=16; $1" | bc) + + # Convert decimal to base 36 + local base36="" + while [ "$(echo "$bigint > 0" | bc)" -eq 1 ]; do + remainder=$(echo "$bigint % 36" | bc) + bigint=$(echo "$bigint / 36" | bc) + + if [ "$remainder" -lt 10 ]; then + base36="${remainder}${base36}" + else + # Convert remainder (10-35) to corresponding ASCII letter ('a'-'z') + val=$((remainder + 87)) + char=$(echo "$val" | awk '{printf "%c", $1}') + base36="${char}${base36}" + fi + done + echo "$base36" +} + +deep_hash_object() { + local obj="$1" + + # Compute SHA-224 hash and convert to uppercase for consistency + local hash_hex + hash_hex=$(echo "$obj" | sha224sum | awk '{print toupper($1)}') + + # Encode the hash to base36 + local base36_hash + base36_hash=$(base36encode "${hash_hex}") + echo "${base36_hash}" +} + +# Function to generate a name based on the base string and hashed object +generate_name() { + local base="$1" + local obj="$2" + local max_name_length=63 # Define the maximum name length (similar to DNS limits) + + # Generate a hash from the object using deep_hash_object function + local hash_str + hash_str=$(deep_hash_object "$obj") + + # Check if the combined length exceeds the maximum length + if [ $((${#base} + ${#hash_str})) -gt $max_name_length ]; then + # Truncate the base string + base="${base:0:$((max_name_length - ${#hash_str} - 1))}" + fi + + # Return the concatenated string + echo "${base}-${hash_str}" +} \ No newline at end of file diff --git a/hack/tools/catalogs/lib/manifests.sh b/hack/tools/catalogs/lib/manifests.sh new file mode 100644 index 0000000000..55a448f559 --- /dev/null +++ b/hack/tools/catalogs/lib/manifests.sh @@ -0,0 +1,121 @@ +# Library of functions for generating kube manifests + +# Function to generate the target install namespace +generate_namespace() { + cat <&2 + container_id=$($CONTAINER_RUNTIME create --quiet --entrypoint="/bin/bash" "$image") + if [ -z "$container_id" ]; then + echo "Failed to create container from image '$image'." >&2 + exit 1 + fi + + echo "${container_id}" +} + +unpack() { + local container_id="${1}" + local output_dir="${2}" + + # Extract the directory from the "operators.operatorframework.io.bundle.manifests.v1" label + local manifest_dir + echo "Unpacking bundle to ${output_dir}" >&2 + manifest_dir=$($CONTAINER_RUNTIME inspect --format '{{ index .Config.Labels "operators.operatorframework.io.bundle.manifests.v1" }}' "$container_id") + + if [ -z "$manifest_dir" ]; then + echo "No manifest directory label found on the image." + $CONTAINER_RUNTIME rm "$container_id" + exit 1 + fi + + # Copy files from the container to a temporary directory + $CONTAINER_RUNTIME cp "$container_id:$manifest_dir/." "$output_dir" > /dev/null + + # Clean up the container + $CONTAINER_RUNTIME rm "$container_id" > /dev/null +} \ No newline at end of file diff --git a/hack/tools/catalogs/lib/utils.sh b/hack/tools/catalogs/lib/utils.sh new file mode 100644 index 0000000000..bebbf27cba --- /dev/null +++ b/hack/tools/catalogs/lib/utils.sh @@ -0,0 +1,49 @@ +# Library of utility functions + +debug() { + if [ "${DEBUG,,}" != "false" ] && [ -n "$DEBUG" ]; then + echo "DEBUG: $1" >&2 + fi +} + +print-banner() { + local red='\033[91m' + local white='\033[97m' + local reset='\033[0m' + local green='\033[92m' + + echo -e "${white}===========================================================================================================================${reset}" + echo -e "${white}‖${red} ____ __ ______ __ ${white}‖${reset}" + echo -e "${white}‖${red} / __ \ ____ ___ _____ ____ _ / /_ ____ _____ / ____/_____ ____ _ ____ ___ ___ _ __ ____ _____ / /__ ${white}‖${reset}" + echo -e "${white}‖${red} / / / // __ \ / _ \ / ___// __ \`// __// __ \ / ___/ / /_ / ___// __ \`// __ \`__ \ / _ \| | /| / // __ \ / ___// //_/ ${white}‖${reset}" + echo -e "${white}‖${red} / /_/ // /_/ // __// / / /_/ // /_ / /_/ // / / __/ / / / /_/ // / / / / // __/| |/ |/ // /_/ // / / ,< ${white}‖${reset}" + echo -e "${white}‖${red} \____// .___/ \___//_/ \__,_/ \__/ \____//_/ /_/ /_/ \__,_//_/ /_/ /_/ \___/ |__/|__/ \____//_/ /_/|_| ${white}‖${reset}" + echo -e "${white}‖${red} /_/${green} OLM v1 ${white}‖${reset}" + echo -e "${white}===========================================================================================================================${reset}" +} + +assert-commands() { + for cmd in "$@"; do + if ! command -v "$cmd" &>/dev/null; then + echo "Required command '$cmd' not found in PATH" >&2 + exit 1 + fi + done +} + +assert-container-runtime() { + if [ -z "$CONTAINER_RUNTIME" ]; then + if command -v podman &>/dev/null; then + export CONTAINER_RUNTIME="podman" + elif command -v docker &>/dev/null; then + export CONTAINER_RUNTIME="docker" + else + echo "No container runtime found in PATH. If not using docker or podman, please set the CONTAINER_RUNTIME environment variable to your container runtime" >&2 + exit 1 + fi + fi + if ! command -v "$CONTAINER_RUNTIME" &>/dev/null; then + echo "Configured container runtime '$CONTAINER_RUNTIME' not found in PATH" >&2 + exit 1 + fi +} \ No newline at end of file diff --git a/hack/tools/catalogs/list-compatible-bundles b/hack/tools/catalogs/list-compatible-bundles new file mode 100755 index 0000000000..8e0560414f --- /dev/null +++ b/hack/tools/catalogs/list-compatible-bundles @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +# Get the directory of the current script +SCRIPT_ROOT=$(dirname "$(realpath "$0")") +source "${SCRIPT_ROOT}/lib/utils.sh" + +# Check required tools are installed +assert-commands jq + +usage() { + print-banner + echo "" + echo "Usage: $0 [-r ] < " + echo "" + echo "Filter Catalog FBC in stdin for OLMv1 (potentially) supported bundles" + echo "" + echo "Examples:" + echo " # Filter for all OLMv1 (potentially) supported bundles" + echo " $0 < operatorhubio-catalog.json" + echo "" + echo " # Filter for all OLMv1 (potentially) supported bundles that contain argocd in the package name" + echo " $0 -r argocd < operatorhubio-catalog.json" + echo "" + echo "NOTE: OLM v1 currently only supports bundles that fulfill the following criteria: " + echo " - Support AllNamespaces install model" + echo " - Don't have dependencies" + echo " - Don't contain webhooks" + echo " - Don't interact with the OperatorConditions API (only verifiable at runtime)" + echo "WARNING: These results may include bundles with webhooks, which are incompatible" +} + +# Parse the optional regex argument +REGEX="" +while getopts "r:" opt; do + case ${opt} in + r ) + REGEX=$OPTARG + ;; + \? ) + usage + exit 1 + ;; + esac +done +shift $((OPTIND -1)) + +# Select bundle documents +function select-bundle-documents() { + jq 'select(.schema == "olm.bundle")' +} + +# Select bundles that declare AllNamespace install mode +# or declare nothing at all (older released bundles sans "olm.csv.metadata" property) +function that-support-allnamespace-install-mode() { + jq 'select( + all(.properties[].type; . != "olm.csv.metadata") or + (.properties[]? | + select(.type == "olm.csv.metadata" and + (.value.installModes[]? | + select(.type == "AllNamespaces" and .supported == true) + ) + ) + ) + )' +} + +# Select bundles without dependencies +function that-dont-have-dependencies() { + jq 'select(all(.properties[].type; . != "olm.package.required" and . != "olm.gvk.required"))' +} + +# Select the "olm.package" property from the bundles +# This contains the packageName and version information +function extract-olm-package-property() { + jq '.properties[] | select(.type == "olm.package") |.value' +} + +# Group packages by name and collect versions into an array +function group-versions-by-package-name() { + jq -s 'group_by(.packageName) | map({packageName: .[0].packageName, versions: map(.version)})' | regex_it +} + +# Apply regex on name +function filter-by-regex-if-necessary() { + jq --arg regex "$REGEX" ' + if $regex != "" then + map(select(.packageName | test($regex))) + else + . + end' +} + +cat - | select-bundle-documents | that-support-allnamespace-install-mode | that-dont-have-dependencies | group-versions-by-package-name | filter-by-regex-if-necessary + +echo "NOTE: OLM v1 currently only supports bundles that support AllNamespaces install model, don't have dependencies, and don't contain webhooks" >&2 +echo "WARNING: These results may include bundles with webhooks, which are incompatible" >&2 \ No newline at end of file diff --git a/hack/tools/catalogs/unpack-bundle b/hack/tools/catalogs/unpack-bundle new file mode 100755 index 0000000000..684c90d48f --- /dev/null +++ b/hack/tools/catalogs/unpack-bundle @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +# Get the directory of the current script +SCRIPT_ROOT=$(dirname "$(realpath "$0")") + +source "${SCRIPT_ROOT}/lib/unpack.sh" +source "${SCRIPT_ROOT}/lib/utils.sh" + +# Check there is a container runtime (podman, or docker) +# If neither are found, check CONTAINER_RUNTIME is set and exists in PATH +assert-container-runtime + +usage() { + print-banner + echo "" + echo "Usage: $0 [-o output_directory] bundle_image" + echo "" + echo "Unpack a bundle image to a directory``" + echo "" + echo "Examples:" + echo " # Unpack argcocd-operator bundle image to a temporary directory" + echo " $0 quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3" + echo "" + echo " # Unpack the bundle image to a specific directory" + echo " $0 -o argocd-manifests quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3" +} + +# Initialize variables +output_directory=$(mktemp -d) +bundle_image="" + +# Parse command line arguments +while getopts "o:" opt; do + case ${opt} in + o) + output_directory=${OPTARG} + ;; + *) + usage + exit 1 + ;; + esac +done +shift $((OPTIND -1)) + +# Check if bundle image argument is provided or read from stdin +if [ -z "$1" ]; then + if [ -t 0 ]; then + echo "Error: Docker image name is required." + usage + else + read -r bundle_image + fi +else + bundle_image="$1" +fi + +if [ -z "${bundle_image}" ]; then + echo "Error: Docker image name is required." + usage + exit 1 +fi + +# Validate output directory +if [ ! -d "$output_directory" ]; then + mkdir -p "$output_directory" +fi + +# Unpack the bundle image +container_id="$(create_container "$bundle_image")" +unpack "${container_id}" "${output_directory}" + +echo "${output_directory}" \ No newline at end of file From 33f6abf07c88435276ce298907ea7d72fabb2b06 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Mon, 16 Sep 2024 16:38:25 -0400 Subject: [PATCH 014/694] switch back to use of trusted CA, add extra test for mirror registry (#1274) Signed-off-by: Joe Lanford --- Makefile | 4 +- .../registries_conf_configmap.yaml | 3 +- hack/test/image-registry.sh | 17 +- test/e2e/cluster_extension_install_test.go | 157 ++++++++++-------- .../catalogs/test-catalog-v1/catalog.yaml | 21 +++ 5 files changed, 115 insertions(+), 87 deletions(-) diff --git a/Makefile b/Makefile index ffc8b7b6a6..746fdfb6b0 100644 --- a/Makefile +++ b/Makefile @@ -165,9 +165,8 @@ test-unit: $(SETUP_ENVTEST) #HELP Run the unit tests $(UNIT_TEST_DIRS) \ -test.gocoverdir=$(ROOT_DIR)/coverage/unit -E2E_REGISTRY_CERT_REF := ClusterIssuer/olmv1-ca # By default, we'll use a trusted CA for the registry. image-registry: ## Setup in-cluster image registry - ./hack/test/image-registry.sh $(E2E_REGISTRY_NAMESPACE) $(E2E_REGISTRY_NAME) $(E2E_REGISTRY_CERT_REF) + ./hack/test/image-registry.sh $(E2E_REGISTRY_NAMESPACE) $(E2E_REGISTRY_NAME) build-push-e2e-catalog: ## Build the testdata catalog used for e2e tests and push it to the image registry ./hack/test/build-push-e2e-catalog.sh $(E2E_REGISTRY_NAMESPACE) $(LOCAL_REGISTRY_HOST)/$(E2E_TEST_CATALOG_V1) @@ -182,7 +181,6 @@ build-push-e2e-catalog: ## Build the testdata catalog used for e2e tests and pus test-e2e: KIND_CLUSTER_NAME := operator-controller-e2e test-e2e: KUSTOMIZE_BUILD_DIR := config/overlays/e2e test-e2e: GO_BUILD_FLAGS := -cover -test-e2e: E2E_REGISTRY_CERT_REF := Issuer/selfsigned-issuer test-e2e: run image-registry build-push-e2e-catalog registry-load-bundles e2e e2e-coverage kind-clean #HELP Run e2e test suite on local kind cluster .PHONY: extension-developer-e2e diff --git a/config/components/registries-conf/registries_conf_configmap.yaml b/config/components/registries-conf/registries_conf_configmap.yaml index df33edb411..2604c78f56 100644 --- a/config/components/registries-conf/registries_conf_configmap.yaml +++ b/config/components/registries-conf/registries_conf_configmap.yaml @@ -6,6 +6,5 @@ metadata: data: registries.conf: | [[registry]] - prefix = "docker-registry.operator-controller-e2e.svc.cluster.local:5000" - insecure = true + prefix = "mirrored-registry.operator-controller-e2e.svc.cluster.local:5000" location = "docker-registry.operator-controller-e2e.svc.cluster.local:5000" diff --git a/hack/test/image-registry.sh b/hack/test/image-registry.sh index 1187de77d8..ac3e68eb7c 100755 --- a/hack/test/image-registry.sh +++ b/hack/test/image-registry.sh @@ -16,7 +16,7 @@ Argument Descriptions: format of 'Issuer/' or 'ClusterIssuer/' " -if [[ "$#" -ne 3 ]]; then +if [[ "$#" -ne 2 ]]; then echo "Illegal number of arguments passed" echo "${help}" exit 1 @@ -24,23 +24,12 @@ fi namespace=$1 name=$2 -certRef=$3 - -echo "CERT_REF: ${certRef}" kubectl apply -f - << EOF apiVersion: v1 kind: Namespace metadata: name: ${namespace} ---- - apiVersion: cert-manager.io/v1 - kind: Issuer - metadata: - name: selfsigned-issuer - namespace: ${namespace} - spec: - selfSigned: {} --- apiVersion: cert-manager.io/v1 kind: Certificate @@ -57,8 +46,8 @@ spec: algorithm: ECDSA size: 256 issuerRef: - name: ${certRef#*/} - kind: ${certRef%/*} + name: olmv1-ca + kind: ClusterIssuer group: cert-manager.io --- apiVersion: apps/v1 diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index ba568c6074..0798ded08a 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -218,77 +218,98 @@ func testCleanup(t *testing.T, cat *catalogd.ClusterCatalog, clusterExtension *o } func TestClusterExtensionInstallRegistry(t *testing.T) { - t.Log("When a cluster extension is installed from a catalog") - t.Log("When the extension bundle format is registry+v1") - - clusterExtension, extensionCatalog, sa := testInit(t) - defer testCleanup(t, extensionCatalog, clusterExtension, sa) - defer getArtifactsOutput(t) - - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ - SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ - PackageName: "prometheus", - Selector: metav1.LabelSelector{ - MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, - }, - }, + type testCase struct { + name string + packageName string + } + for _, tc := range []testCase{ + { + name: "no registry configuration necessary", + packageName: "prometheus", }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + { + // NOTE: This test requires an extra configuration in /etc/containers/registries.conf, which is mounted + // for this e2e via the ./config/components/registries-conf kustomize component as part of the e2e overlay. + // The goal here is to prove that "mirrored-registry.operator-controller-e2e.svc.cluster.local:5000" is + // mapped to the "real" registry hostname ("docker-registry.operator-controller-e2e.svc.cluster.local:5000"). + name: "package requires mirror registry configuration in /etc/containers/registries.conf", + packageName: "prometheus-mirrored", }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Log("When a cluster extension is installed from a catalog") + t.Log("When the extension bundle format is registry+v1") + + clusterExtension, extensionCatalog, sa := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension, sa) + defer getArtifactsOutput(t) + + clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ + Source: ocv1alpha1.SourceConfig{ + SourceType: "Catalog", + Catalog: &ocv1alpha1.CatalogSource{ + PackageName: tc.packageName, + Selector: metav1.LabelSelector{ + MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, + }, + }, + }, + Install: ocv1alpha1.ClusterExtensionInstallConfig{ + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, + }, + }, + } + t.Log("It resolves the specified package with correct bundle path") + t.Log("By creating the ClusterExtension resource") + require.NoError(t, c.Create(context.Background(), clusterExtension)) + + t.Log("By eventually reporting a successful resolution and bundle path") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "resolved to") + assert.Equal(ct, + &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ + Name: fmt.Sprintf("%s-operator.1.2.0", tc.packageName), + Version: "1.2.0", + }}, + clusterExtension.Status.Resolution, + ) + }, pollDuration, pollInterval) + + t.Log("By eventually reporting a successful unpacked") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Regexp(ct, "^unpacked .* successfully", cond.Message) + }, pollDuration, pollInterval) + + t.Log("By eventually installing the package successfully") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "Installed bundle") + assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle) + }, pollDuration, pollInterval) + }) } - t.Log("It resolves the specified package with correct bundle path") - t.Log("By creating the ClusterExtension resource") - require.NoError(t, c.Create(context.Background(), clusterExtension)) - - t.Log("By eventually reporting a successful resolution and bundle path") - require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.2.0", - Version: "1.2.0", - }}, - clusterExtension.Status.Resolution, - ) - }, pollDuration, pollInterval) - - t.Log("By eventually reporting a successful unpacked") - require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Regexp(ct, "^unpacked .* successfully", cond.Message) - }, pollDuration, pollInterval) - - t.Log("By eventually installing the package successfully") - require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "Installed bundle") - assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle) - }, pollDuration, pollInterval) } func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { diff --git a/testdata/catalogs/test-catalog-v1/catalog.yaml b/testdata/catalogs/test-catalog-v1/catalog.yaml index 9ec6beae5e..41c843d72c 100644 --- a/testdata/catalogs/test-catalog-v1/catalog.yaml +++ b/testdata/catalogs/test-catalog-v1/catalog.yaml @@ -48,3 +48,24 @@ properties: value: packageName: prometheus version: 1.2.0 + +--- +schema: olm.package +name: prometheus-mirrored +defaultChannel: beta +--- +schema: olm.channel +name: beta +package: prometheus-mirrored +entries: + - name: prometheus-mirrored-operator.1.2.0 +--- +schema: olm.bundle +name: prometheus-mirrored-operator.1.2.0 +package: prometheus-mirrored +image: mirrored-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v1.2.0 +properties: + - type: olm.package + value: + packageName: prometheus-mirrored + version: 1.2.0 From b1f5cefe91ce5e5479397017d08c22ef218b0e15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:56:34 +0200 Subject: [PATCH 015/694] :seedling: Bump github.com/operator-framework/catalogd from 0.26.0 to 0.27.0 (#1268) * :seedling: Bump github.com/operator-framework/catalogd Bumps [github.com/operator-framework/catalogd](https://github.com/operator-framework/catalogd) from 0.26.0 to 0.27.0. - [Release notes](https://github.com/operator-framework/catalogd/releases) - [Changelog](https://github.com/operator-framework/catalogd/blob/main/.goreleaser.yml) - [Commits](https://github.com/operator-framework/catalogd/compare/v0.26.0...v0.27.0) --- updated-dependencies: - dependency-name: github.com/operator-framework/catalogd dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * remove usage of catalogd InsecureSkipTLSVerify; regen docs --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joe Lanford --- docs/refs/api/catalogd-api-reference.md | 2 -- go.mod | 2 +- go.sum | 4 ++-- test/e2e/e2e_suite_test.go | 5 ++--- test/extension-developer-e2e/extension_developer_test.go | 3 +-- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/refs/api/catalogd-api-reference.md b/docs/refs/api/catalogd-api-reference.md index 89d130a9fa..89a72ffd53 100644 --- a/docs/refs/api/catalogd-api-reference.md +++ b/docs/refs/api/catalogd-api-reference.md @@ -132,9 +132,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `ref` _string_ | ref contains the reference to a container image containing Catalog contents. | | | -| `pullSecret` _string_ | pullSecret contains the name of the image pull secret in the namespace that catalogd is deployed. | | | | `pollInterval` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#duration-v1-meta)_ | pollInterval indicates the interval at which the image source should be polled for new content,
specified as a duration (e.g., "5m", "1h", "24h", "etc".). Note that PollInterval may not be
specified for a catalog image referenced by a sha256 digest. | | Format: duration
| -| `insecureSkipTLSVerify` _boolean_ | insecureSkipTLSVerify indicates that TLS certificate validation should be skipped.
If this option is specified, the HTTPS protocol will still be used to
fetch the specified image reference.
This should not be used in a production environment. | | | #### ResolvedCatalogSource diff --git a/go.mod b/go.mod index e241b05313..259b4f4d11 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.26.0 + github.com/operator-framework/catalogd v0.27.0 github.com/operator-framework/helm-operator-plugins v0.5.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index b991c83981..21504d954f 100644 --- a/go.sum +++ b/go.sum @@ -537,8 +537,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.26.0 h1:RDzNEv631o7WgkXGfFrOCiFBaBMwK621/vinRuOS2LI= -github.com/operator-framework/catalogd v0.26.0/go.mod h1:pR03BacyPJPeVk6KM5OW6CLOoqkHzvyncQSZmiO3+IQ= +github.com/operator-framework/catalogd v0.27.0 h1:KYQs8RZTppbVidcCboicL+W1Ch2UzyGTq6uqaM73MkY= +github.com/operator-framework/catalogd v0.27.0/go.mod h1:SFRcc/UT1hrMiONDJclSGmENLBHgvB/i/7AkBxiMvrM= github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 539260e983..b292c46538 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -53,9 +53,8 @@ func createTestCatalog(ctx context.Context, name string, imageRef string) (*cata Source: catalogd.CatalogSource{ Type: catalogd.SourceTypeImage, Image: &catalogd.ImageSource{ - Ref: imageRef, - InsecureSkipTLSVerify: true, - PollInterval: &metav1.Duration{Duration: time.Second}, + Ref: imageRef, + PollInterval: &metav1.Duration{Duration: time.Second}, }, }, }, diff --git a/test/extension-developer-e2e/extension_developer_test.go b/test/extension-developer-e2e/extension_developer_test.go index 91e7391a9c..2db90b674a 100644 --- a/test/extension-developer-e2e/extension_developer_test.go +++ b/test/extension-developer-e2e/extension_developer_test.go @@ -47,8 +47,7 @@ func TestExtensionDeveloper(t *testing.T) { Source: catalogd.CatalogSource{ Type: catalogd.SourceTypeImage, Image: &catalogd.ImageSource{ - Ref: os.Getenv("CATALOG_IMG"), - InsecureSkipTLSVerify: true, + Ref: os.Getenv("CATALOG_IMG"), }, }, }, From e16eb7690bc826890a2613e5677f97128bb3e568 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Tue, 17 Sep 2024 07:43:41 -0400 Subject: [PATCH 016/694] bump golangci-lint to 1.61.0; stop using deprecated output-format (#1275) Signed-off-by: Joe Lanford --- .bingo/Variables.mk | 6 +++--- .bingo/golangci-lint.mod | 2 +- .bingo/golangci-lint.sum | 32 ++++++++++++++++++++++++++++++++ .bingo/variables.env | 2 +- .github/workflows/sanity.yaml | 2 +- 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index ea22690e3f..c1dbd815f1 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -35,11 +35,11 @@ $(CRD_REF_DOCS): $(BINGO_DIR)/crd-ref-docs.mod @echo "(re)installing $(GOBIN)/crd-ref-docs-v0.1.0" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=crd-ref-docs.mod -o=$(GOBIN)/crd-ref-docs-v0.1.0 "github.com/elastic/crd-ref-docs" -GOLANGCI_LINT := $(GOBIN)/golangci-lint-v1.60.3 +GOLANGCI_LINT := $(GOBIN)/golangci-lint-v1.61.0 $(GOLANGCI_LINT): $(BINGO_DIR)/golangci-lint.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/golangci-lint-v1.60.3" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v1.60.3 "github.com/golangci/golangci-lint/cmd/golangci-lint" + @echo "(re)installing $(GOBIN)/golangci-lint-v1.61.0" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v1.61.0 "github.com/golangci/golangci-lint/cmd/golangci-lint" GORELEASER := $(GOBIN)/goreleaser-v1.26.2 $(GORELEASER): $(BINGO_DIR)/goreleaser.mod diff --git a/.bingo/golangci-lint.mod b/.bingo/golangci-lint.mod index 25f0891d94..1ec0d30c17 100644 --- a/.bingo/golangci-lint.mod +++ b/.bingo/golangci-lint.mod @@ -4,4 +4,4 @@ go 1.22.1 toolchain go1.22.5 -require github.com/golangci/golangci-lint v1.60.3 // cmd/golangci-lint +require github.com/golangci/golangci-lint v1.61.0 // cmd/golangci-lint diff --git a/.bingo/golangci-lint.sum b/.bingo/golangci-lint.sum index 5a272b75f5..b39751610d 100644 --- a/.bingo/golangci-lint.sum +++ b/.bingo/golangci-lint.sum @@ -50,6 +50,8 @@ github.com/Abirdcfly/dupword v0.0.11 h1:z6v8rMETchZXUIuHxYNmlUAuKuB21PeaSymTed16 github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA= github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11Ba8= github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI= +github.com/Abirdcfly/dupword v0.1.1 h1:Bsxe0fIw6OwBtXMIncaTxCLHYO5BB+3mcsR5E8VXloY= +github.com/Abirdcfly/dupword v0.1.1/go.mod h1:B49AcJdTYYkpd4HjgAcutNGG9HZ2JWwKunH9Y2BA6sM= github.com/Antonboom/errname v0.1.10 h1:RZ7cYo/GuZqjr1nuJLNe8ZH+a+Jd9DaZzttWzak9Bls= github.com/Antonboom/errname v0.1.10/go.mod h1:xLeiCIrvVNpUtsN0wxAh05bNIZpqE22/qDMnTBTttiA= github.com/Antonboom/errname v0.1.12 h1:oh9ak2zUtsLp5oaEd/erjB4GPu9w19NyoIskZClDcQY= @@ -82,6 +84,8 @@ github.com/Crocmagnon/fatcontext v0.2.2 h1:OrFlsDdOj9hW/oBEJBNSuH7QWf+E9WPVHw+x5 github.com/Crocmagnon/fatcontext v0.2.2/go.mod h1:WSn/c/+MMNiD8Pri0ahRj0o9jVpeowzavOQplBJw6u0= github.com/Crocmagnon/fatcontext v0.4.0 h1:4ykozu23YHA0JB6+thiuEv7iT6xq995qS1vcuWZq0tg= github.com/Crocmagnon/fatcontext v0.4.0/go.mod h1:ZtWrXkgyfsYPzS6K3O88va6t2GEglG93vnII/F94WC0= +github.com/Crocmagnon/fatcontext v0.5.2 h1:vhSEg8Gqng8awhPju2w7MKHqMlg4/NI+gSDHtR3xgwA= +github.com/Crocmagnon/fatcontext v0.5.2/go.mod h1:87XhRMaInHP44Q7Tlc7jkgKKB7kZAOPiDkFMdKCC+74= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 h1:+r1rSv4gvYn0wmRjC8X7IAzX8QezqtFV9m0MUHFJgts= @@ -94,6 +98,8 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/OpenPeeDeeP/depguard/v2 v2.1.0 h1:aQl70G173h/GZYhWf36aE5H0KaujXfVMnn/f1kSDVYY= github.com/OpenPeeDeeP/depguard/v2 v2.1.0/go.mod h1:PUBgk35fX4i7JDmwzlJwJ+GMe6NfO1723wmJMgPThNQ= github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= @@ -170,6 +176,8 @@ github.com/ckaznocha/intrange v0.1.1 h1:gHe4LfqCspWkh8KpJFs20fJz3XRHFBFUV9yI7Itu github.com/ckaznocha/intrange v0.1.1/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE= github.com/ckaznocha/intrange v0.1.2 h1:3Y4JAxcMntgb/wABQ6e8Q8leMd26JbX2790lIss9MTI= github.com/ckaznocha/intrange v0.1.2/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE= +github.com/ckaznocha/intrange v0.2.0 h1:FykcZuJ8BD7oX93YbO1UY9oZtkRbp+1/kJcDjkefYLs= +github.com/ckaznocha/intrange v0.2.0/go.mod h1:r5I7nUlAAG56xmkOpw4XVr16BXhwYTUdcuRFeevn1oE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -184,6 +192,8 @@ github.com/daixiang0/gci v0.12.3 h1:yOZI7VAxAGPQmkb1eqt5g/11SUlwoat1fSblGLmdiQc= github.com/daixiang0/gci v0.12.3/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw= github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= +github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -263,6 +273,8 @@ github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsM github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= +github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -324,6 +336,8 @@ github.com/golangci/golangci-lint v1.59.1 h1:CRRLu1JbhK5avLABFJ/OHVSQ0Ie5c4ulsOI github.com/golangci/golangci-lint v1.59.1/go.mod h1:jX5Oif4C7P0j9++YB2MMJmoNrb01NJ8ITqKWNLewThg= github.com/golangci/golangci-lint v1.60.3 h1:l38A5de24ZeDlcFF+EB7m3W5joPD99/hS5SIHJPyZa0= github.com/golangci/golangci-lint v1.60.3/go.mod h1:J4vOpcjzRI+lDL2DKNGBZVB3EQSBfCBCMpaydWLtJNo= +github.com/golangci/golangci-lint v1.61.0 h1:VvbOLaRVWmyxCnUIMTbf1kDsaJbTzH20FAMXTAlQGu8= +github.com/golangci/golangci-lint v1.61.0/go.mod h1:e4lztIrJJgLPhWvFPDkhiMwEFRrWlmFbrZea3FsJyN8= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= @@ -564,6 +578,8 @@ github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeB github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -606,6 +622,8 @@ github.com/quasilyte/go-ruleguard v0.3.19 h1:tfMnabXle/HzOb5Xe9CUZYWXKfkS1KwRmZy github.com/quasilyte/go-ruleguard v0.3.19/go.mod h1:lHSn69Scl48I7Gt9cX3VrbsZYvYiBYszZOZW4A+oTEw= github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL6CH/BYs= github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= +github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo= +github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= @@ -624,6 +642,8 @@ github.com/ryancurrah/gomodguard v1.3.2 h1:CuG27ulzEB1Gu5Dk5gP8PFxSOZ3ptSdP5iI/3 github.com/ryancurrah/gomodguard v1.3.2/go.mod h1:LqdemiFomEjcxOqirbQCb3JFvSxH2JUYMerTFd3sF2o= github.com/ryancurrah/gomodguard v1.3.3 h1:eiSQdJVNr9KTNxY2Niij8UReSwR8Xrte3exBrAZfqpg= github.com/ryancurrah/gomodguard v1.3.3/go.mod h1:rsKQjj4l3LXe8N344Ow7agAy5p9yjsWOtRzUMYmA0QY= +github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU= +github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= github.com/ryanrolds/sqlclosecheck v0.4.0 h1:i8SX60Rppc1wRuyQjMciLqIzV3xnoHB7/tXbr6RGYNI= github.com/ryanrolds/sqlclosecheck v0.4.0/go.mod h1:TBRRjzL31JONc9i4XMinicuo+s+E8yKZ5FN8X3G6CKQ= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= @@ -650,6 +670,8 @@ github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 h1:rnO6Zp1YMQ github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9/go.mod h1:dg7lPlu/xK/Ut9SedURCoZbVCR4yC7fM65DtH9/CDHs= github.com/securego/gosec/v2 v2.20.1-0.20240822074752-ab3f6c1c83a0 h1:VqD4JMoqwuuCz8GZlBDsIDyE6K4YUsWJpbNtuOWHoFk= github.com/securego/gosec/v2 v2.20.1-0.20240822074752-ab3f6c1c83a0/go.mod h1:iyeMMRw8QEmueUSZ2VqmkQMiDyDcobfPnG00CV/NWdE= +github.com/securego/gosec/v2 v2.21.2 h1:deZp5zmYf3TWwU7A7cR2+SolbTpZ3HQiwFqnzQyEl3M= +github.com/securego/gosec/v2 v2.21.2/go.mod h1:au33kg78rNseF5PwPnTWhuYBFf534bvJRvOrgZ/bFzU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -723,6 +745,8 @@ github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw= github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/tetafro/godot v1.4.17 h1:pGzu+Ye7ZUEFx7LHU0dAKmCOXWsPjl7qA6iMGndsjPs= +github.com/tetafro/godot v1.4.17/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= @@ -834,6 +858,8 @@ golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcH golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 h1:J74nGeMgeFnYQJN59eFwh06jX/V8g0lB7LWpjSLxtgU= @@ -880,6 +906,8 @@ golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1021,6 +1049,8 @@ golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1046,6 +1076,8 @@ golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/.bingo/variables.env b/.bingo/variables.env index 669b17ad40..5c36ce81fa 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -14,7 +14,7 @@ CONTROLLER_GEN="${GOBIN}/controller-gen-v0.16.1" CRD_REF_DOCS="${GOBIN}/crd-ref-docs-v0.1.0" -GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.60.3" +GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.61.0" GORELEASER="${GOBIN}/goreleaser-v1.26.2" diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml index 5b7a641a36..601fbd14da 100644 --- a/.github/workflows/sanity.yaml +++ b/.github/workflows/sanity.yaml @@ -29,4 +29,4 @@ jobs: go-version-file: "go.mod" - name: Run golangci linting checks - run: make lint GOLANGCI_LINT_ARGS="--out-format github-actions" + run: make lint GOLANGCI_LINT_ARGS="--out-format colored-line-number" From 11c590cb52087272d10de57975cb6faff3842360 Mon Sep 17 00:00:00 2001 From: Brett Tofel Date: Tue, 17 Sep 2024 14:19:42 -0400 Subject: [PATCH 017/694] =?UTF-8?q?=F0=9F=93=96=20[Docs]=20Downgrade=20an?= =?UTF-8?q?=20extension=20(#1277)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update downgrade guide for ClusterExtension Significantly expanded the documentation to include detailed steps on downgrading a ClusterExtension, including disabling CRD safety checks and overriding upgrade constraints. Added prerequisites, troubleshooting, and post-downgrade verification sections. Signed-off-by: Brett Tofel * Refactor location of downgrade doc Signed-off-by: Brett Tofel * Wording corrections on intro paragraph Signed-off-by: Brett Tofel * Update intro & CRD safety check procedure Enhanced downgrade guide with new risks and update steps. Replaced manual `kubectl edit` step with a `kubectl patch` command for disabling CRD safety checks. Signed-off-by: Brett Tofel --------- Signed-off-by: Brett Tofel --- README.md | 2 +- docs/drafts/Tasks/downgrading-an-extension.md | 3 - docs/drafts/Tasks/upgrading-an-extension.md | 2 +- docs/drafts/downgrading-an-extension.md | 199 ++++++++++++++++++ 4 files changed, 201 insertions(+), 5 deletions(-) delete mode 100644 docs/drafts/Tasks/downgrading-an-extension.md create mode 100644 docs/drafts/downgrading-an-extension.md diff --git a/README.md b/README.md index 2754862092..c4a1ce3afb 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ kubectl patch clusterextension argocd --type='merge' -p '{"spec": {"source": {"c ``` -For information on the downgrade process, see [here](./docs/drafts/Tasks/downgrading-an-extension.md). +For information on the downgrade process, see [here](docs/drafts/downgrading-an-extension.md). ### Uninstall the Cluster Extension diff --git a/docs/drafts/Tasks/downgrading-an-extension.md b/docs/drafts/Tasks/downgrading-an-extension.md deleted file mode 100644 index 58d9ec846e..0000000000 --- a/docs/drafts/Tasks/downgrading-an-extension.md +++ /dev/null @@ -1,3 +0,0 @@ -# Downgrade an Extension - -Placeholder. This topic is under development. \ No newline at end of file diff --git a/docs/drafts/Tasks/upgrading-an-extension.md b/docs/drafts/Tasks/upgrading-an-extension.md index 648093e87a..ec13c73178 100644 --- a/docs/drafts/Tasks/upgrading-an-extension.md +++ b/docs/drafts/Tasks/upgrading-an-extension.md @@ -2,7 +2,7 @@ Existing extensions can be upgraded by updating the version field in the ClusterExtension resource. -For information on downgrading an extension, see [Downgrade an Extension](downgrading-an-extension). +For information on downgrading an extension, see [Downgrade an Extension](../downgrading-an-extension.md). ## Prerequisites diff --git a/docs/drafts/downgrading-an-extension.md b/docs/drafts/downgrading-an-extension.md new file mode 100644 index 0000000000..c372ce8e2e --- /dev/null +++ b/docs/drafts/downgrading-an-extension.md @@ -0,0 +1,199 @@ + +# Downgrade a ClusterExtension + +## Introduction + +Downgrading a `ClusterExtension` involves reverting the extension to a previously available version. This process may be necessary due to compatibility issues, unexpected behavior in the newer version, or specific feature requirements only available in an earlier release. However, downgrading carries inherent risks, such as potential data loss, issues with new CRD versions, and possible breakage of clients that rely on the newer version. Users should carefully consider these risks and be confident in their decision to proceed with the downgrade. This guide provides step-by-step instructions for performing a downgrade, including overrides to bypass default constraints and disable CRD safety checks. + +## Prerequisites + +Before initiating the downgrade process, ensure the following prerequisites are met: + +- **Backup Configurations:** Always back up your current configurations and data to prevent potential loss during the downgrade. +- **Access Rights:** Ensure you have the necessary permissions to modify `ClusterExtension` resources and perform administrative tasks. +- **Version Availability:** Verify that the target downgrade version is available in your catalogs. +- **Compatibility Check:** Ensure that the target version is compatible with your current system and other dependencies. + +## Steps to Downgrade + +### 1. Disabling the CRD Upgrade Safety Check + +Custom Resource Definitions (CRDs) ensure that the resources used by the `ClusterExtension` are valid and consistent. During a downgrade, the CRD Upgrade Safety check might prevent reverting to an incompatible version. Disabling the CRD Upgrade Safety check allows the downgrade to proceed without these validations. + +**Disable CRD Safety Check Configuration:** + +Add the `crdUpgradeSafety` field and set its `policy` to `Disabled` in the `ClusterExtension` resource under the `preflight` section. + +**Example:** + +```yaml +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension +metadata: + name: example-extension +spec: + install: + preflight: + crdUpgradeSafety: + policy: Disabled + namespace: argocd + serviceAccount: + name: argocd-installer + source: + sourceType: Catalog + catalog: + packageName: argocd-operator + version: 0.6.0 + upgradeConstraintPolicy: SelfCertified +``` + +** Disable CRD Upgrade Safety Check:** + +**Patch the ClusterExtension Resource:** + + ```bash + kubectl patch clusterextension --patch '{"spec":{"install":{"preflight":{"crdUpgradeSafety":{"policy":"Disabled"}}}}}' --type=merge + ``` + Kubernetes will apply the updated configuration, disabling CRD safety checks during the downgrade process. + +### 2. Ignoring Catalog Provided Upgrade Constraints + +By default, Operator Lifecycle Manager (OLM) enforces upgrade constraints based on semantic versioning and catalog definitions. To allow downgrades, you need to override these constraints. + +**Override Configuration:** + +Set the `upgradeConstraintPolicy` to `SelfCertified` in the `ClusterExtension` resource. This configuration permits downgrades, sidegrades, and any version changes without adhering to the predefined upgrade paths. + +**Example:** + +```yaml +apiVersion: olm.operatorframework.io/v1alpha1 +kind: ClusterExtension +metadata: + name: example-extension +spec: + source: + sourceType: Catalog + catalog: + packageName: argocd-operator + version: 0.6.0 + upgradeConstraintPolicy: SelfCertified + install: + namespace: argocd + serviceAccount: + name: argocd-installer +``` + +**Command Example:** + +If you prefer using the command line, you can use `kubectl` to modify the upgrade constraint policy. + +```bash +kubectl patch clusterextension --patch '{"spec":{"upgradeConstraintPolicy":"SelfCertified"}}' --type=merge +``` + +### 3. Executing the Downgrade + +Once the CRD safety checks are disabled and upgrade constraints are set, you can proceed with the actual downgrade. + +1. **Edit the ClusterExtension Resource:** + + Modify the `ClusterExtension` custom resource to specify the target version and adjust the upgrade constraints. + + ```bash + kubectl edit clusterextension + ``` + +2. **Update the Version:** + + Within the YAML editor, update the `spec` section as follows: + + ```yaml + apiVersion: olm.operatorframework.io/v1alpha1 + kind: ClusterExtension + metadata: + name: + spec: + source: + sourceType: Catalog + catalog: + packageName: + version: + install: + namespace: + serviceAccount: + name: + ``` + + - **`version`:** Specify the target version you wish to downgrade to. + +3. **Apply the Changes:** + + Save and exit the editor. Kubernetes will apply the changes and initiate the downgrade process. + +### 4. Post-Downgrade Verification + +After completing the downgrade, verify that the `ClusterExtension` is functioning as expected. + +**Verification Steps:** + +1. **Check the Status of the ClusterExtension:** + + ```bash + kubectl get clusterextension -o yaml + ``` + + Ensure that the `status` reflects the target version and that there are no error messages. + +2. **Validate CRD Integrity:** + + Confirm that all CRDs associated with the `ClusterExtension` are correctly installed and compatible with the downgraded version. + + ```bash + kubectl get crd | grep + ``` + +3. **Test Extension Functionality:** + + Perform functional tests to ensure that the extension operates correctly in its downgraded state. + +4. **Monitor Logs:** + + Check the logs of the operator managing the `ClusterExtension` for any warnings or errors. + + ```bash + kubectl logs deployment/ -n + ``` + +## Troubleshooting + +During the downgrade process, you might encounter issues. Below are common problems and their solutions: + +### Downgrade Fails Due to Version Constraints + +**Solution:** + +- Ensure that the `upgradeConstraintPolicy` is set to `SelfCertified`. +- Verify that the target version exists in the catalog. +- Check for typos or incorrect version numbers in the configuration. + +### CRD Compatibility Issues + +**Solution:** + +- Review the changes in CRDs between versions to ensure compatibility. +- If disabling the CRD safety check, ensure that the downgraded version can handle the existing CRDs without conflicts. +- Consider manually reverting CRDs if necessary, but proceed with caution to avoid data loss. + +### Extension Becomes Unresponsive After Downgrade + +**Solution:** + +- Restore from the backup taken before the downgrade. +- Investigate logs for errors related to the downgraded version. +- Verify that all dependencies required by the downgraded version are satisfied. + +## Additional Resources + +- [Semantic Versioning Specification](https://semver.org/) +- [Manually Verified Upgrades and Downgrades](https://github.com/operator-framework/operator-controller/blob/main/docs/drafts/upgrade-support.md#manually-verified-upgrades-and-downgrades) From d1441462ee0213302685bf4deae8371e8ecd8157 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Tue, 17 Sep 2024 16:23:19 -0400 Subject: [PATCH 018/694] use controller-runtime Terminal error instead of our custom Unrecoverable error (#1276) Signed-off-by: Joe Lanford --- internal/rukpak/source/containers_image.go | 7 ++++--- internal/rukpak/source/containers_image_test.go | 7 ++++--- internal/rukpak/source/unpacker.go | 11 ----------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/internal/rukpak/source/containers_image.go b/internal/rukpak/source/containers_image.go index 60841ea498..2394e5f307 100644 --- a/internal/rukpak/source/containers_image.go +++ b/internal/rukpak/source/containers_image.go @@ -21,6 +21,7 @@ import ( "github.com/containers/image/v5/types" "github.com/opencontainers/go-digest" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" ) type ContainersImageRegistry struct { @@ -36,7 +37,7 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour } if bundle.Image == nil { - return nil, NewUnrecoverable(fmt.Errorf("error parsing bundle, bundle %s has a nil image source", bundle.Name)) + return nil, reconcile.TerminalError(fmt.Errorf("error parsing bundle, bundle %s has a nil image source", bundle.Name)) } ////////////////////////////////////////////////////// @@ -46,7 +47,7 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour ////////////////////////////////////////////////////// imgRef, err := reference.ParseNamed(bundle.Image.Ref) if err != nil { - return nil, NewUnrecoverable(fmt.Errorf("error parsing image reference %q: %w", bundle.Image.Ref, err)) + return nil, reconcile.TerminalError(fmt.Errorf("error parsing image reference %q: %w", bundle.Image.Ref, err)) } canonicalRef, err := resolveCanonicalRef(ctx, imgRef, i.SourceContext) @@ -180,7 +181,7 @@ func resolveCanonicalRef(ctx context.Context, imgRef reference.Named, imageCtx * srcRef, err := docker.NewReference(imgRef) if err != nil { - return nil, NewUnrecoverable(fmt.Errorf("error creating reference: %w", err)) + return nil, reconcile.TerminalError(fmt.Errorf("error creating reference: %w", err)) } imgSrc, err := srcRef.NewImageSource(ctx, imageCtx) diff --git a/internal/rukpak/source/containers_image_test.go b/internal/rukpak/source/containers_image_test.go index 1d1c42dcde..32b43d2fc3 100644 --- a/internal/rukpak/source/containers_image_test.go +++ b/internal/rukpak/source/containers_image_test.go @@ -20,6 +20,7 @@ import ( "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/operator-framework/operator-controller/internal/rukpak/source" ) @@ -143,7 +144,7 @@ func TestUnpackNameOnlyImageReference(t *testing.T) { // Attempt to pull and unpack the image _, err := unpacker.Unpack(context.Background(), bundleSource) assert.ErrorContains(t, err, "tag or digest is needed") - assert.ErrorAs(t, err, &source.Unrecoverable{}) + assert.ErrorIs(t, err, reconcile.TerminalError(nil)) } func TestUnpackUnservedTaggedImageReference(t *testing.T) { @@ -225,7 +226,7 @@ func TestUnpackInvalidNilImage(t *testing.T) { result, err := unpacker.Unpack(context.Background(), bundleSource) assert.Nil(t, result) assert.ErrorContains(t, err, "nil image source") - assert.ErrorAs(t, err, &source.Unrecoverable{}) + assert.ErrorIs(t, err, reconcile.TerminalError(nil)) assert.NoDirExists(t, filepath.Join(unpacker.BaseCachePath, bundleSource.Name)) } @@ -244,7 +245,7 @@ func TestUnpackInvalidImageRef(t *testing.T) { result, err := unpacker.Unpack(context.Background(), bundleSource) assert.Nil(t, result) assert.ErrorContains(t, err, "error parsing image reference") - assert.ErrorAs(t, err, &source.Unrecoverable{}) + assert.ErrorIs(t, err, reconcile.TerminalError(nil)) assert.NoDirExists(t, filepath.Join(unpacker.BaseCachePath, bundleSource.Name)) } diff --git a/internal/rukpak/source/unpacker.go b/internal/rukpak/source/unpacker.go index 5d2b5f633d..f56de0010a 100644 --- a/internal/rukpak/source/unpacker.go +++ b/internal/rukpak/source/unpacker.go @@ -13,17 +13,6 @@ type ImageSource struct { Ref string } -// Unrecoverable represents an error that can not be recovered -// from without user intervention. When this error is returned -// the request should not be requeued. -type Unrecoverable struct { - error -} - -func NewUnrecoverable(err error) Unrecoverable { - return Unrecoverable{err} -} - // Unpacker unpacks bundle content, either synchronously or asynchronously and // returns a Result, which conveys information about the progress of unpacking // the bundle content. From 06424ef35cd2225495ce0f89faaf90a6e9749309 Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Tue, 17 Sep 2024 22:29:20 +0200 Subject: [PATCH 019/694] Fix `http.DefaultClient` (#1278) We do not want to modify `http.DefaultClient` because it can cause undesired effects in other places. Signed-off-by: Mikalai Radchuk --- internal/catalogmetadata/cache/cache_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/catalogmetadata/cache/cache_test.go b/internal/catalogmetadata/cache/cache_test.go index 05ea28ec59..51b5547219 100644 --- a/internal/catalogmetadata/cache/cache_test.go +++ b/internal/catalogmetadata/cache/cache_test.go @@ -212,8 +212,9 @@ func TestFilesystemCache(t *testing.T) { cacheDir := t.TempDir() tt.tripper.content = make(fstest.MapFS) maps.Copy(tt.tripper.content, tt.contents) - httpClient := http.DefaultClient - httpClient.Transport = tt.tripper + httpClient := &http.Client{ + Transport: tt.tripper, + } c := cache.NewFilesystemCache(cacheDir, func() (*http.Client, error) { return httpClient, nil }) From c73da43732e625e693921018b2c5d23528a82f30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:00:50 -0700 Subject: [PATCH 020/694] :seedling: Bump mkdocs-material from 9.5.34 to 9.5.35 (#1281) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.34 to 9.5.35. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.34...9.5.35) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e48d17d152..f73f017637 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ markdown2==2.5.0 MarkupSafe==2.1.5 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.5.34 +mkdocs-material==9.5.35 mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 From df0e8481f09e6beac92d05c9ff62e31675d383b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:48:25 -0700 Subject: [PATCH 021/694] :seedling: Bump platformdirs from 4.3.3 to 4.3.6 (#1280) Bumps [platformdirs](https://github.com/tox-dev/platformdirs) from 4.3.3 to 4.3.6. - [Release notes](https://github.com/tox-dev/platformdirs/releases) - [Changelog](https://github.com/tox-dev/platformdirs/blob/main/CHANGES.rst) - [Commits](https://github.com/tox-dev/platformdirs/compare/4.3.3...4.3.6) --- updated-dependencies: - dependency-name: platformdirs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f73f017637..378cb23457 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 pathspec==0.12.1 -platformdirs==4.3.3 +platformdirs==4.3.6 Pygments==2.18.0 pymdown-extensions==10.9 pyquery==2.0.1 From 34821fa76280945f263d72e2cd118821c8554f91 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Thu, 19 Sep 2024 10:27:48 -0400 Subject: [PATCH 022/694] fix: do not silently drop finalizer updates when status is also updated (#1283) --- .../controllers/clusterextension_controller.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index dc3dd72475..6e06f849d4 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -116,17 +116,24 @@ func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Req // Do checks before any Update()s, as Update() may modify the resource structure! updateStatus := !equality.Semantic.DeepEqual(existingExt.Status, reconciledExt.Status) updateFinalizers := !equality.Semantic.DeepEqual(existingExt.Finalizers, reconciledExt.Finalizers) + + // If any unexpected fields have changed, panic before updating the resource unexpectedFieldsChanged := checkForUnexpectedFieldChange(*existingExt, *reconciledExt) + if unexpectedFieldsChanged { + panic("spec or metadata changed by reconciler") + } + // Save the finalizers off to the side. If we update the status, the reconciledExt will be updated + // to contain the new state of the ClusterExtension, which contains the status update, but (critically) + // does not contain the finalizers. After the status update, we need to re-add the finalizers to the + // reconciledExt before updating the object. + finalizers := reconciledExt.Finalizers if updateStatus { if err := r.Client.Status().Update(ctx, reconciledExt); err != nil { updateError = errors.Join(updateError, fmt.Errorf("error updating status: %v", err)) } } - - if unexpectedFieldsChanged { - panic("spec or metadata changed by reconciler") - } + reconciledExt.Finalizers = finalizers if updateFinalizers { if err := r.Client.Update(ctx, reconciledExt); err != nil { From f3e2ce0727136bbfe572e750fef1ce1e38795552 Mon Sep 17 00:00:00 2001 From: Brett Tofel Date: Thu, 19 Sep 2024 16:12:33 -0400 Subject: [PATCH 023/694] =?UTF-8?q?=F0=9F=93=96=20[Docs]=20Reorganize=20de?= =?UTF-8?q?veloper=20documentation=20(#1279)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Reorganize developer documentation Reordered sections and added MacOS setup into a dedicated section. Improved structure for better readability and provided clear instructions for installing and using necessary tools. Signed-off-by: Brett Tofel * Incorporate trgeiger comments Signed-off-by: Brett Tofel * Remove How-to; clarify Kind use; grammar Signed-off-by: Brett Tofel * Fix missing backticks Signed-off-by: Brett Tofel --------- Signed-off-by: Brett Tofel --- docs/drafts/developer.md | 101 +++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 58 deletions(-) diff --git a/docs/drafts/developer.md b/docs/drafts/developer.md index dbaf3999b6..4ce60ac2c9 100644 --- a/docs/drafts/developer.md +++ b/docs/drafts/developer.md @@ -1,10 +1,24 @@ + ## Getting Started -You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster. + +The following `make run` starts a [KIND](https://sigs.k8s.io/kind) cluster for you to get a local cluster for testing, see the manual install steps below for how to run against a remote cluster. > [!NOTE] > Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows). +> +> If you are on MacOS, see [Special Setup for MacOS](#special-setup-for-macos). + +### Quickstart Installation + +First, you need to install the CRDs and the operator-controller into a new [KIND cluster](https://kind.sigs.k8s.io/). You can do this by running: + +```sh +make run +``` + +This will build a local container image of the operator-controller, create a new KIND cluster and then deploy onto that cluster. This will also deploy the catalogd and cert-manager dependencies. -### Installation +### To Install Any Given Release > [!CAUTION] > Operator-Controller depends on [cert-manager](https://cert-manager.io/). Running the following command @@ -16,27 +30,7 @@ The latest version of Operator Controller can be installed with the following co curl -L -s https://github.com/operator-framework/operator-controller/releases/latest/download/install.sh | bash -s ``` -### Additional setup on Macintosh computers -On Macintosh computers some additional setup is necessary to install and configure compatible tooling. - -#### Install Homebrew and tools -Follow the instructions to [installing Homebrew](https://docs.brew.sh/Installation) and then execute the following to install tools: - -```sh -brew install bash gnu-tar gsed -``` - -#### Configure your shell -Modify your login shell's `PATH` to prefer the new tools over those in the existing environment. This example should work either with `zsh` (in $HOME/.zshrc) or `bash` (in $HOME/.bashrc): - -```sh -for bindir in `find $(brew --prefix)/opt -type d -follow -name gnubin -print` -do - export PATH=$bindir:$PATH -done -``` - -### Running on the cluster +### Manual Step-by-Step Installation 1. Install Instances of Custom Resources: ```sh @@ -69,21 +63,6 @@ To undeploy the controller from the cluster: make undeploy ``` -### How it works -This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). - -It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/) -which provide a reconcile function responsible for synchronizing resources until the desired state is reached on the cluster. - -### Test It Out - -Install the CRDs and the operator-controller into a new [KIND cluster](https://kind.sigs.k8s.io/): -```sh -make run -``` -This will build a local container image of the operator-controller, create a new KIND cluster and then deploy onto that cluster. -This will also deploy the catalogd and cert-manager dependencies. - ### Modifying the API definitions If you are editing the API definitions, generate the manifests such as CRs or CRDs using: @@ -91,11 +70,13 @@ If you are editing the API definitions, generate the manifests such as CRs or CR make manifests ``` +--- + **NOTE:** Run `make help` for more information on all potential `make` targets. -### Rapid iterative development with Tilt +### Rapid Iterative Development with Tilt -If you are developing against the combined ecosystem of catalogd + operator-controller you will want to take advantage of `tilt`: +If you are developing against the combined ecosystem of catalogd + operator-controller, you will want to take advantage of `tilt`: [Tilt](https://tilt.dev) is a tool that enables rapid iterative development of containerized workloads. @@ -105,8 +86,6 @@ Here is an example workflow without Tilt for modifying some source code and test 2. Build the container image. 3. Either push the image to a registry or load it into your kind cluster. 4. Deploy all the appropriate Kubernetes manifests for your application. - 1. Or, if this is an update, you'd instead scale the Deployment to 0 replicas, scale back to 1, and wait for the - new pod to be running. This process can take minutes, depending on how long each step takes. @@ -125,9 +104,7 @@ Follow Tilt's [instructions](https://docs.tilt.dev/install.html) for installatio ### Installing catalogd operator-controller requires -[catalogd](https://github.com/operator-framework/catalogd). Please make sure it's installed, either normally or via -their own Tiltfiles, before proceeding. If you want to use Tilt, make sure you specify a unique `--port` flag to each -`tilt up` invocation. +[catalogd](https://github.com/operator-framework/catalogd). Please make sure it's installed, either normally or via its own Tiltfile., before proceeding. If you want to use Tilt, make sure you specify a unique `--port` flag to each `tilt up` invocation. ### Install tilt-support Repo @@ -171,25 +148,33 @@ v0.33.1, built 2023-06-28 (ctrl-c) to exit ``` -Typically, you'll want to press the space bar to have it open the UI in your web browser. +At the end of the installation process, the command output will prompt you to press the space bar to open the web UI, which provides a useful overview of all the installed components. + +--- + +## Special Setup for MacOS + +Some additional setup is necessary on Macintosh computers to install and configure compatible tooling. -Shortly after starting, Tilt processes the `Tiltfile`, resulting in: +### Install Homebrew and tools +Follow the instructions to [install Homebrew](https://docs.brew.sh/Installation), and then execute the following command to install the required tools: -- Building the go binaries -- Building the images -- Loading the images into kind -- Running kustomize and applying everything except the Deployments that reference the images above -- Modifying the Deployments to use the just-built images -- Creating the Deployments +```sh +brew install bash gnu-tar gsed +``` -### Making code changes +### Configure your shell +To configure your shell, either add this to your bash or zsh profile (e.g., in $HOME/.bashrc or $HOME/.zshrc), or run the following command in the terminal: -Any time you change any of the files listed in the `deps` section in the `_binary` `local_resource`, -Tilt automatically rebuilds the go binary. As soon as the binary is rebuilt, Tilt pushes it (and only it) into the -appropriate running container, and then restarts the process. +```sh +for bindir in `find $(brew --prefix)/opt -type d -follow -name gnubin -print` +do + export PATH=$bindir:$PATH +done +``` --- ## Contributing -Refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for more information. \ No newline at end of file +Refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for more information. From e0e5e5cb13b6b27c689413f551898943d7875a75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:48:39 +0200 Subject: [PATCH 024/694] :seedling: Bump github.com/operator-framework/catalogd (#1285) Bumps [github.com/operator-framework/catalogd](https://github.com/operator-framework/catalogd) from 0.27.0 to 0.28.0. - [Release notes](https://github.com/operator-framework/catalogd/releases) - [Changelog](https://github.com/operator-framework/catalogd/blob/main/.goreleaser.yml) - [Commits](https://github.com/operator-framework/catalogd/compare/v0.27.0...v0.28.0) --- updated-dependencies: - dependency-name: github.com/operator-framework/catalogd dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: Per Goncalves da Silva Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/refs/api/catalogd-api-reference.md | 45 +++++++++++++------------ go.mod | 4 +-- go.sum | 8 ++--- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/docs/refs/api/catalogd-api-reference.md b/docs/refs/api/catalogd-api-reference.md index 89a72ffd53..07be6f3c64 100644 --- a/docs/refs/api/catalogd-api-reference.md +++ b/docs/refs/api/catalogd-api-reference.md @@ -26,7 +26,7 @@ Package v1alpha1 contains API Schema definitions for the core v1alpha1 API group -CatalogSource contains the sourcing information for a Catalog +CatalogSource is a discriminated union of possible sources for a Catalog. @@ -35,15 +35,16 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `type` _[SourceType](#sourcetype)_ | type defines the kind of Catalog content being sourced. | | Enum: [image]
Required: \{\}
| -| `image` _[ImageSource](#imagesource)_ | image is the catalog image that backs the content of this catalog. | | | +| `type` _[SourceType](#sourcetype)_ | type is a required reference to the type of source the catalog is sourced from.

Allowed values are ["image"]

When this field is set to "image", the ClusterCatalog content will be sourced from an OCI image.
When using an image source, the image field must be set and must be the only field defined for this type. | | Enum: [image]
Required: \{\}
| +| `image` _[ImageSource](#imagesource)_ | image is used to configure how catalog contents are sourced from an OCI image. This field must be set when type is set to "image" and must be the only field defined for this type. | | | #### ClusterCatalog -ClusterCatalog is the Schema for the ClusterCatalogs API +ClusterCatalog enables users to make File-Based Catalog (FBC) catalog data available to the cluster. +For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs @@ -94,8 +95,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `source` _[CatalogSource](#catalogsource)_ | source is the source of a Catalog that contains catalog metadata in the FBC format
https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs | | | -| `priority` _integer_ | priority is used as the tie-breaker between bundles selected from different catalogs; a higher number means higher priority. | 0 | | +| `source` _[CatalogSource](#catalogsource)_ | source is a required field that allows the user to define the source of a Catalog that contains catalog metadata in the File-Based Catalog (FBC) format.

Below is a minimal example of a ClusterCatalogSpec that sources a catalog from an image:

source:
type: image
image:
ref: quay.io/operatorhubio/catalog:latest

For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs | | | +| `priority` _integer_ | priority is an optional field that allows the user to define a priority for a ClusterCatalog.
A ClusterCatalog's priority is used by clients as a tie-breaker between ClusterCatalogs that meet the client's requirements.
For example, in the case where multiple ClusterCatalogs provide the same bundle.
A higher number means higher priority. Negative number as also accepted.
When omitted, the default priority is 0. | 0 | | #### ClusterCatalogStatus @@ -111,18 +112,18 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions store the status conditions of the ClusterCatalog instances | | | -| `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source | | | -| `contentURL` _string_ | contentURL is a cluster-internal address that on-cluster components
can read the content of a catalog from | | | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterCatalog.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human-readable message that further elaborates on the state of the condition.

The current set of condition types are:
- "Unpacked", epresents whether, or not, the catalog contents have been successfully unpacked.
- "Deleted", represents whether, or not, the catalog contents have been successfully deleted.

The current set of reasons are:
- "UnpackPending", this reason is set on the "Unpack" condition when unpacking the catalog has not started.
- "Unpacking", this reason is set on the "Unpack" condition when the catalog is being unpacked.
- "UnpackSuccessful", this reason is set on the "Unpack" condition when unpacking the catalog is successful and the catalog metadata is available to the cluster.
- "FailedToStore", this reason is set on the "Unpack" condition when an error has been encountered while storing the contents of the catalog.
- "FailedToDelete", this reason is set on the "Delete" condition when an error has been encountered while deleting the contents of the catalog. | | | +| `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source based on the source type.

Below is an example of a resolved source for an image source:
resolvedSource:

image:
lastPollAttempt: "2024-09-10T12:22:13Z"
lastUnpacked: "2024-09-10T12:22:13Z"
ref: quay.io/operatorhubio/catalog:latest
resolvedRef: quay.io/operatorhubio/catalog@sha256:c7392b4be033da629f9d665fec30f6901de51ce3adebeff0af579f311ee5cf1b
type: image | | | +| `contentURL` _string_ | contentURL is a cluster-internal URL from which on-cluster components
can read the content of a catalog | | | | `observedGeneration` _integer_ | observedGeneration is the most recent generation observed for this ClusterCatalog. It corresponds to the
ClusterCatalog's generation, which is updated on mutation by the API Server. | | | -| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | LastUnpacked represents the time when the
ClusterCatalog object was last unpacked. | | | +| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastUnpacked represents the time when the
ClusterCatalog object was last unpacked. | | | #### ImageSource -ImageSource contains information required for sourcing a Catalog from an OCI image +ImageSource enables users to define the information required for sourcing a Catalog from an OCI image @@ -131,15 +132,15 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `ref` _string_ | ref contains the reference to a container image containing Catalog contents. | | | -| `pollInterval` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#duration-v1-meta)_ | pollInterval indicates the interval at which the image source should be polled for new content,
specified as a duration (e.g., "5m", "1h", "24h", "etc".). Note that PollInterval may not be
specified for a catalog image referenced by a sha256 digest. | | Format: duration
| +| `ref` _string_ | ref is a required field that allows the user to define the reference to a container image containing Catalog contents.
Examples:
ref: quay.io/operatorhubio/catalog:latest # image reference
ref: quay.io/operatorhubio/catalog@sha256:c7392b4be033da629f9d665fec30f6901de51ce3adebeff0af579f311ee5cf1b # image reference with sha256 digest | | | +| `pollInterval` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#duration-v1-meta)_ | pollInterval is an optional field that allows the user to set the interval at which the image source should be polled for new content.
It must be specified as a duration.
It must not be specified for a catalog image referenced by a sha256 digest.
Examples:
pollInterval: 1h # poll the image source every hour
pollInterval: 30m # poll the image source every 30 minutes
pollInterval: 1h30m # poll the image source every 1 hour and 30 minutes

When omitted, the image will not be polled for new content. | | Format: duration
| #### ResolvedCatalogSource -ResolvedCatalogSource contains the information about a sourced Catalog +ResolvedCatalogSource is a discriminated union of resolution information for a Catalog. @@ -148,15 +149,15 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `type` _[SourceType](#sourcetype)_ | type defines the kind of Catalog content that was sourced. | | Enum: [image]
Required: \{\}
| -| `image` _[ResolvedImageSource](#resolvedimagesource)_ | image is the catalog image that backs the content of this catalog. | | | +| `type` _[SourceType](#sourcetype)_ | type is a reference to the type of source the catalog is sourced from.

It will be set to one of the following values: ["image"].

When this field is set to "image", information about the resolved image source will be set in the 'image' field. | | Enum: [image]
Required: \{\}
| +| `image` _[ResolvedImageSource](#resolvedimagesource)_ | image is a field containing resolution information for a catalog sourced from an image. | | | #### ResolvedImageSource -ResolvedImageSource contains information about the sourced Catalog +ResolvedImageSource provides information about the resolved source of a Catalog sourced from an image. @@ -165,17 +166,17 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `ref` _string_ | ref contains the reference to a container image containing Catalog contents. | | | -| `resolvedRef` _string_ | resolvedRef contains the resolved sha256 image ref containing Catalog contents. | | | -| `lastPollAttempt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastPollAtempt is the time when the source resolved was last polled for new content. | | | -| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | LastUnpacked is the time when the catalog contents were successfully unpacked. | | | +| `ref` _string_ | ref is the reference to a container image containing Catalog contents. | | | +| `resolvedRef` _string_ | resolvedRef is the resolved sha256 image ref containing Catalog contents. | | | +| `lastPollAttempt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastPollAttempt is the time when the source image was last polled for new content. | | | +| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastUnpacked is the last time when the Catalog contents were successfully unpacked. | | | #### SourceType _Underlying type:_ _string_ - +SourceType defines the type of source used for catalogs. diff --git a/go.mod b/go.mod index 259b4f4d11..2cfba4906f 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.27.0 + github.com/operator-framework/catalogd v0.28.0 github.com/operator-framework/helm-operator-plugins v0.5.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 @@ -181,7 +181,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/proglottis/gpgme v0.1.3 // indirect - github.com/prometheus/client_golang v1.20.3 // indirect + github.com/prometheus/client_golang v1.20.4 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect diff --git a/go.sum b/go.sum index 21504d954f..39fb376d8d 100644 --- a/go.sum +++ b/go.sum @@ -537,8 +537,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.27.0 h1:KYQs8RZTppbVidcCboicL+W1Ch2UzyGTq6uqaM73MkY= -github.com/operator-framework/catalogd v0.27.0/go.mod h1:SFRcc/UT1hrMiONDJclSGmENLBHgvB/i/7AkBxiMvrM= +github.com/operator-framework/catalogd v0.28.0 h1:4M8Jpe443EAWJ2L0knTidq3haM+ac6seT+0IrAGULng= +github.com/operator-framework/catalogd v0.28.0/go.mod h1:cikCg3BwFMyMrRhMXN6cxpv03Cm/EMV3rHX5yi2w8vo= github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= @@ -571,8 +571,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= -github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= From 134386ddd08ff936ac1fa890cfc1eab9d5874955 Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:59:56 +0200 Subject: [PATCH 025/694] =?UTF-8?q?=F0=9F=90=9B=20Remove=20cache=20when=20?= =?UTF-8?q?catalog=20is=20deleted=20(#1207)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add `Remove` into `filesystemCache` Enables us to deletes cache directory for a given catalog from the filesystem. Signed-off-by: Mikalai Radchuk * Add a finaliser to ClusterCatalog New finaliser allows us to remove catalog cache from filesystem on catalog deletion. Signed-off-by: Mikalai Radchuk --------- Signed-off-by: Mikalai Radchuk --- cmd/manager/main.go | 14 ++- config/base/rbac/role.yaml | 1 + internal/catalogmetadata/cache/cache.go | 27 +++++- internal/catalogmetadata/cache/cache_test.go | 62 +++++++++++- .../controllers/clustercatalog_controller.go | 76 +++++++++++++++ .../clustercatalog_controller_test.go | 95 +++++++++++++++++++ 6 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 internal/controllers/clustercatalog_controller.go create mode 100644 internal/controllers/clustercatalog_controller_test.go diff --git a/cmd/manager/main.go b/cmd/manager/main.go index db25c3ad07..55951ca836 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -219,9 +219,10 @@ func main() { setupLog.Error(err, "unable to create catalogs cache directory") os.Exit(1) } - catalogClient := catalogclient.New(cache.NewFilesystemCache(catalogsCachePath, func() (*http.Client, error) { + cacheFetcher := cache.NewFilesystemCache(catalogsCachePath, func() (*http.Client, error) { return httputil.BuildHTTPClient(certPoolWatcher) - })) + }) + catalogClient := catalogclient.New(cacheFetcher) resolver := &resolve.CatalogResolver{ WalkCatalogsFunc: resolve.CatalogWalker( @@ -277,6 +278,15 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "ClusterExtension") os.Exit(1) } + + if err = (&controllers.ClusterCatalogReconciler{ + Client: cl, + Cache: cacheFetcher, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ClusterCatalog") + os.Exit(1) + } + //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/config/base/rbac/role.yaml b/config/base/rbac/role.yaml index 38d394780f..ee0a598334 100644 --- a/config/base/rbac/role.yaml +++ b/config/base/rbac/role.yaml @@ -21,6 +21,7 @@ rules: resources: - clustercatalogs verbs: + - get - list - watch - apiGroups: diff --git a/internal/catalogmetadata/cache/cache.go b/internal/catalogmetadata/cache/cache.go index f5c8a52ebb..e986c9714e 100644 --- a/internal/catalogmetadata/cache/cache.go +++ b/internal/catalogmetadata/cache/cache.go @@ -25,7 +25,7 @@ var _ client.Fetcher = &filesystemCache{} // - IF cached it will verify the cache is up to date. If it is up to date it will return // the cached contents, if not it will fetch the new contents from the catalogd HTTP // server and update the cached contents. -func NewFilesystemCache(cachePath string, clientFunc func() (*http.Client, error)) client.Fetcher { +func NewFilesystemCache(cachePath string, clientFunc func() (*http.Client, error)) *filesystemCache { return &filesystemCache{ cachePath: cachePath, mutex: sync.RWMutex{}, @@ -80,7 +80,7 @@ func (fsc *filesystemCache) FetchCatalogContents(ctx context.Context, catalog *c return nil, fmt.Errorf("error: catalog %q has a nil status.resolvedSource.image value", catalog.Name) } - cacheDir := filepath.Join(fsc.cachePath, catalog.Name) + cacheDir := fsc.cacheDir(catalog.Name) fsc.mutex.RLock() if data, ok := fsc.cacheDataByCatalogName[catalog.Name]; ok { if catalog.Status.ResolvedSource.Image.ResolvedRef == data.ResolvedRef { @@ -166,3 +166,26 @@ func (fsc *filesystemCache) FetchCatalogContents(ctx context.Context, catalog *c return os.DirFS(cacheDir), nil } + +// Remove deletes cache directory for a given catalog from the filesystem +func (fsc *filesystemCache) Remove(catalogName string) error { + cacheDir := fsc.cacheDir(catalogName) + + fsc.mutex.Lock() + defer fsc.mutex.Unlock() + + if _, exists := fsc.cacheDataByCatalogName[catalogName]; !exists { + return nil + } + + if err := os.RemoveAll(cacheDir); err != nil { + return fmt.Errorf("error removing cache directory: %v", err) + } + + delete(fsc.cacheDataByCatalogName, catalogName) + return nil +} + +func (fsc *filesystemCache) cacheDir(catalogName string) string { + return filepath.Join(fsc.cachePath, catalogName) +} diff --git a/internal/catalogmetadata/cache/cache_test.go b/internal/catalogmetadata/cache/cache_test.go index 51b5547219..74f7d79c0a 100644 --- a/internal/catalogmetadata/cache/cache_test.go +++ b/internal/catalogmetadata/cache/cache_test.go @@ -10,12 +10,14 @@ import ( "io/fs" "maps" "net/http" + "os" "path/filepath" "testing" "testing/fstest" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" @@ -62,7 +64,7 @@ var defaultFS = fstest.MapFS{ "fake1/olm.channel/stable.json": &fstest.MapFile{Data: []byte(stableChannel)}, } -func TestFilesystemCache(t *testing.T) { +func TestFilesystemCacheFetchCatalogContents(t *testing.T) { type test struct { name string catalog *catalogd.ClusterCatalog @@ -245,6 +247,64 @@ func TestFilesystemCache(t *testing.T) { } } +func TestFilesystemCacheRemove(t *testing.T) { + testCatalog := &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-catalog", + }, + Status: catalogd.ClusterCatalogStatus{ + ResolvedSource: &catalogd.ResolvedCatalogSource{ + Type: catalogd.SourceTypeImage, + Image: &catalogd.ResolvedImageSource{ + ResolvedRef: "fake/catalog@sha256:fakesha", + }, + }, + }, + } + + ctx := context.Background() + cacheDir := t.TempDir() + + tripper := &mockTripper{} + tripper.content = make(fstest.MapFS) + maps.Copy(tripper.content, defaultFS) + httpClient := &http.Client{ + Transport: tripper, + } + c := cache.NewFilesystemCache(cacheDir, func() (*http.Client, error) { + return httpClient, nil + }) + + catalogCachePath := filepath.Join(cacheDir, testCatalog.Name) + + t.Log("Remove cache before it exists") + require.NoDirExists(t, catalogCachePath) + err := c.Remove(testCatalog.Name) + require.NoError(t, err) + assert.NoDirExists(t, catalogCachePath) + + t.Log("Fetch contents to populate cache") + _, err = c.FetchCatalogContents(ctx, testCatalog) + require.NoError(t, err) + require.DirExists(t, catalogCachePath) + + t.Log("Temporary change permissions to the cache dir to cause error") + require.NoError(t, os.Chmod(catalogCachePath, 0000)) + + t.Log("Remove cache causes an error") + err = c.Remove(testCatalog.Name) + require.ErrorContains(t, err, "error removing cache directory") + require.DirExists(t, catalogCachePath) + + t.Log("Restore directory permissions for successful removal") + require.NoError(t, os.Chmod(catalogCachePath, 0777)) + + t.Log("Remove cache") + err = c.Remove(testCatalog.Name) + require.NoError(t, err) + assert.NoDirExists(t, catalogCachePath) +} + var _ http.RoundTripper = &mockTripper{} type mockTripper struct { diff --git a/internal/controllers/clustercatalog_controller.go b/internal/controllers/clustercatalog_controller.go new file mode 100644 index 0000000000..0f7a26a6ca --- /dev/null +++ b/internal/controllers/clustercatalog_controller.go @@ -0,0 +1,76 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" +) + +type CatalogCacheRemover interface { + Remove(catalogName string) error +} + +// ClusterCatalogReconciler reconciles a ClusterCatalog object +type ClusterCatalogReconciler struct { + client.Client + Cache CatalogCacheRemover +} + +//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clustercatalogs,verbs=get;list;watch + +func (r *ClusterCatalogReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + existingCatalog := &catalogd.ClusterCatalog{} + err := r.Client.Get(ctx, req.NamespacedName, existingCatalog) + if apierrors.IsNotFound(err) { + return ctrl.Result{}, r.Cache.Remove(req.Name) + } + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ClusterCatalogReconciler) SetupWithManager(mgr ctrl.Manager) error { + _, err := ctrl.NewControllerManagedBy(mgr). + For(&catalogd.ClusterCatalog{}, builder.WithPredicates(predicate.Funcs{ + CreateFunc: func(e event.CreateEvent) bool { + return false + }, + UpdateFunc: func(e event.UpdateEvent) bool { + return false + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return true + }, + GenericFunc: func(e event.GenericEvent) bool { + return false + }, + })). + Build(r) + + return err +} diff --git a/internal/controllers/clustercatalog_controller_test.go b/internal/controllers/clustercatalog_controller_test.go new file mode 100644 index 0000000000..762fa15eca --- /dev/null +++ b/internal/controllers/clustercatalog_controller_test.go @@ -0,0 +1,95 @@ +package controllers_test + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + + "github.com/operator-framework/operator-controller/internal/controllers" + "github.com/operator-framework/operator-controller/internal/scheme" +) + +func TestClusterCatalogReconcilerFinalizers(t *testing.T) { + catalogKey := types.NamespacedName{Name: "test-catalog"} + + for _, tt := range []struct { + name string + catalog *catalogd.ClusterCatalog + cacheRemoveFunc func(catalogName string) error + wantCacheRemoveCalled bool + wantErr string + }{ + { + name: "catalog exists", + catalog: &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: catalogKey.Name, + }, + }, + }, + { + name: "catalog does not exist", + cacheRemoveFunc: func(catalogName string) error { + assert.Equal(t, catalogKey.Name, catalogName) + return nil + }, + wantCacheRemoveCalled: true, + }, + { + name: "catalog does not exist - error on removal", + cacheRemoveFunc: func(catalogName string) error { + return errors.New("fake error from remove") + }, + wantCacheRemoveCalled: true, + wantErr: "fake error from remove", + }, + } { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + + clientBuilder := fake.NewClientBuilder().WithScheme(scheme.Scheme) + if tt.catalog != nil { + clientBuilder = clientBuilder.WithObjects(tt.catalog) + } + cl := clientBuilder.Build() + + cacheRemover := &mockCatalogCacheRemover{ + removeFunc: tt.cacheRemoveFunc, + } + + reconciler := &controllers.ClusterCatalogReconciler{ + Client: cl, + Cache: cacheRemover, + } + + result, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catalogKey}) + if tt.wantErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.wantErr) + } + require.Equal(t, ctrl.Result{}, result) + + assert.Equal(t, tt.wantCacheRemoveCalled, cacheRemover.called) + }) + } +} + +type mockCatalogCacheRemover struct { + called bool + removeFunc func(catalogName string) error +} + +func (m *mockCatalogCacheRemover) Remove(catalogName string) error { + m.called = true + return m.removeFunc(catalogName) +} From a752f58b6e4ec3d775f174af6e09efe8d7f61b9e Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Fri, 20 Sep 2024 16:43:47 +0200 Subject: [PATCH 026/694] =?UTF-8?q?=F0=9F=8C=B1=20Bump=20catalogd=20from?= =?UTF-8?q?=20v0.28.0=20to=20v0.29.0=20(#1288)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :seedling: Bump catalogd from v0.28.0 to v0.29.0 Signed-off-by: Per Goncalves da Silva * Update operator-controller for catalogd condition changes Signed-off-by: Per Goncalves da Silva --------- Signed-off-by: Per Goncalves da Silva Co-authored-by: Per Goncalves da Silva --- docs/Tasks/adding-a-catalog.md | 2 +- go.mod | 2 +- go.sum | 4 ++-- hack/tools/catalogs/download-catalog | 2 +- internal/catalogmetadata/client/client.go | 4 ++-- internal/catalogmetadata/client/client_test.go | 12 ++++++------ scripts/install.tpl.sh | 2 +- test/e2e/cluster_extension_install_test.go | 8 ++++---- test/upgrade-e2e/post_upgrade_test.go | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/Tasks/adding-a-catalog.md b/docs/Tasks/adding-a-catalog.md index 12d07adbf9..8158f0d4a7 100644 --- a/docs/Tasks/adding-a-catalog.md +++ b/docs/Tasks/adding-a-catalog.md @@ -113,7 +113,7 @@ This catalog is distributed as an image [quay.io/operatorhubio/catalog](https:// Reason: UnpackSuccessful Status: True Type: Unpacked - Content URL: https://catalogd-catalogserver.olmv1-system.svc/catalogs/operatorhubio/all.json + Content URL: https://catalogd-server.olmv1-system.svc/catalogs/operatorhubio/all.json Observed Generation: 2 Resolved Source: Image: diff --git a/go.mod b/go.mod index 2cfba4906f..8079963ade 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.28.0 + github.com/operator-framework/catalogd v0.29.0 github.com/operator-framework/helm-operator-plugins v0.5.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index 39fb376d8d..5c9316e6fa 100644 --- a/go.sum +++ b/go.sum @@ -537,8 +537,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.28.0 h1:4M8Jpe443EAWJ2L0knTidq3haM+ac6seT+0IrAGULng= -github.com/operator-framework/catalogd v0.28.0/go.mod h1:cikCg3BwFMyMrRhMXN6cxpv03Cm/EMV3rHX5yi2w8vo= +github.com/operator-framework/catalogd v0.29.0 h1:GpeBGUWNGbVTH9l2S4RLi2wRvLAguG/4LYdxqfrwlak= +github.com/operator-framework/catalogd v0.29.0/go.mod h1:cikCg3BwFMyMrRhMXN6cxpv03Cm/EMV3rHX5yi2w8vo= github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= diff --git a/hack/tools/catalogs/download-catalog b/hack/tools/catalogs/download-catalog index cfb7e51a9a..8e997215fc 100755 --- a/hack/tools/catalogs/download-catalog +++ b/hack/tools/catalogs/download-catalog @@ -8,7 +8,7 @@ assert-commands kubectl jq wget # ClusterCatalog coordinates : "${CATALOGD_CATALOGD_SERVICE_NAMESPACE:=olmv1-system}" -: "${CATALOGD_SERVICE_NAME:=catalogd-catalogserver}" +: "${CATALOGD_SERVICE_NAME:=catalogd-server}" : "${CATALOGD_SERVICE_PORT:=443}" # Assumes the service uses HTTPS on port 443 : "${CATALOGD_LOCAL_SERVICE_PORT:=8001}" diff --git a/internal/catalogmetadata/client/client.go b/internal/catalogmetadata/client/client.go index 2b0663d486..6a9efe2858 100644 --- a/internal/catalogmetadata/client/client.go +++ b/internal/catalogmetadata/client/client.go @@ -39,8 +39,8 @@ type Client struct { func (c *Client) GetPackage(ctx context.Context, catalog *catalogd.ClusterCatalog, pkgName string) (*declcfg.DeclarativeConfig, error) { // if the catalog has not been successfully unpacked, report an error. This ensures that our // reconciles are deterministic and wait for all desired catalogs to be ready. - if !meta.IsStatusConditionPresentAndEqual(catalog.Status.Conditions, catalogd.TypeUnpacked, metav1.ConditionTrue) { - return nil, fmt.Errorf("catalog %q is not unpacked", catalog.Name) + if !meta.IsStatusConditionPresentAndEqual(catalog.Status.Conditions, catalogd.TypeServing, metav1.ConditionTrue) { + return nil, fmt.Errorf("catalog %q is not being served", catalog.Name) } catalogFsys, err := c.fetcher.FetchCatalogContents(ctx, catalog) diff --git a/internal/catalogmetadata/client/client_test.go b/internal/catalogmetadata/client/client_test.go index beacc4512f..989b66acc0 100644 --- a/internal/catalogmetadata/client/client_test.go +++ b/internal/catalogmetadata/client/client_test.go @@ -34,13 +34,13 @@ func TestClientNew(t *testing.T) { catalog: &catalogd.ClusterCatalog{ObjectMeta: metav1.ObjectMeta{Name: "catalog-1"}}, fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return testFS, nil }), assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { - assert.ErrorContains(t, err, `catalog "catalog-1" is not unpacked`) + assert.ErrorContains(t, err, `catalog "catalog-1" is not being served`) }, }, { name: "unpacked, fetcher returns error", catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeUnpacked, Status: metav1.ConditionTrue}}}, + Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, }, fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return nil, errors.New("fetch error") }), assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { @@ -50,7 +50,7 @@ func TestClientNew(t *testing.T) { { name: "unpacked, invalid package path", catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeUnpacked, Status: metav1.ConditionTrue}}}, + Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, }, fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return testFS, nil }), pkgName: "/", @@ -61,7 +61,7 @@ func TestClientNew(t *testing.T) { { name: "unpacked, package missing", catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeUnpacked, Status: metav1.ConditionTrue}}}, + Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, }, pkgName: "pkg-missing", fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return testFS, nil }), @@ -73,7 +73,7 @@ func TestClientNew(t *testing.T) { { name: "unpacked, invalid package present", catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeUnpacked, Status: metav1.ConditionTrue}}}, + Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, }, pkgName: "invalid-pkg-present", fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { @@ -89,7 +89,7 @@ func TestClientNew(t *testing.T) { { name: "unpacked, package present", catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeUnpacked, Status: metav1.ConditionTrue}}}, + Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, }, pkgName: "pkg-present", fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return testFS, nil }), diff --git a/scripts/install.tpl.sh b/scripts/install.tpl.sh index e4f95b4312..e8c857266c 100644 --- a/scripts/install.tpl.sh +++ b/scripts/install.tpl.sh @@ -51,7 +51,7 @@ kubectl_wait "olmv1-system" "deployment/catalogd-controller-manager" "60s" if [[ "${install_default_catalogs,,}" != "false" ]]; then kubectl apply -f "https://github.com/operator-framework/catalogd/releases/download/$%7Bcatalogd_version%7D/default-catalogs.yaml" - kubectl wait --for=condition=Unpacked "clustercatalog/operatorhubio" --timeout="60s" + kubectl wait --for=condition=Serving "clustercatalog/operatorhubio" --timeout="60s" fi kubectl apply -f "${operator_controller_manifest}" diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 0798ded08a..58e1b30112 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -614,12 +614,12 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { require.NoError(t, err) require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) - cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeUnpacked) + cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeServing) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, catalogd.ReasonUnpackSuccessful, cond.Reason) + assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) }, pollDuration, pollInterval) t.Log("By eventually reporting a successful resolution and bundle path") @@ -714,12 +714,12 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { require.NoError(t, err) require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) - cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeUnpacked) + cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeServing) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, catalogd.ReasonUnpackSuccessful, cond.Reason) + assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) }, pollDuration, pollInterval) t.Log("By eventually reporting a successful resolution and bundle path") diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go index 751730b8a1..06f9a31b01 100644 --- a/test/upgrade-e2e/post_upgrade_test.go +++ b/test/upgrade-e2e/post_upgrade_test.go @@ -67,12 +67,12 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { var clusterCatalog catalogdv1alpha1.ClusterCatalog assert.NoError(ct, c.Get(ctx, types.NamespacedName{Name: testClusterCatalogName}, &clusterCatalog)) - cond := apimeta.FindStatusCondition(clusterCatalog.Status.Conditions, catalogdv1alpha1.TypeUnpacked) + cond := apimeta.FindStatusCondition(clusterCatalog.Status.Conditions, catalogdv1alpha1.TypeServing) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, catalogdv1alpha1.ReasonUnpackSuccessful, cond.Reason) + assert.Equal(ct, catalogdv1alpha1.ReasonAvailable, cond.Reason) }, time.Minute, time.Second) t.Log("Checking that the ClusterExtension is installed") From dc4141a302b43625f872581bcce4096180adc8a5 Mon Sep 17 00:00:00 2001 From: Brett Tofel Date: Fri, 20 Sep 2024 12:59:55 -0400 Subject: [PATCH 027/694] Improve developer.md (#1289) Replace kube context mention; Improve Mac brew usage Signed-off-by: Brett Tofel --- docs/drafts/developer.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/drafts/developer.md b/docs/drafts/developer.md index 4ce60ac2c9..31959de6c1 100644 --- a/docs/drafts/developer.md +++ b/docs/drafts/developer.md @@ -4,7 +4,7 @@ The following `make run` starts a [KIND](https://sigs.k8s.io/kind) cluster for you to get a local cluster for testing, see the manual install steps below for how to run against a remote cluster. > [!NOTE] -> Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows). +> You will need a container runtime environment, like Docker, or experimentally, Podman, installed, to run Kind. > > If you are on MacOS, see [Special Setup for MacOS](#special-setup-for-macos). @@ -167,7 +167,7 @@ brew install bash gnu-tar gsed To configure your shell, either add this to your bash or zsh profile (e.g., in $HOME/.bashrc or $HOME/.zshrc), or run the following command in the terminal: ```sh -for bindir in `find $(brew --prefix)/opt -type d -follow -name gnubin -print` +for bindir in `find $(brew --prefix)/opt -type d -follow -name gnubin -print -maxdepth 3` do export PATH=$bindir:$PATH done From 62b5c53bbcffb44116c54730406c0bd65a7b4d7c Mon Sep 17 00:00:00 2001 From: Michael Peter Date: Fri, 20 Sep 2024 14:02:33 -0400 Subject: [PATCH 028/694] =?UTF-8?q?=F0=9F=93=96=20[Docs]=20Enable=20mermai?= =?UTF-8?q?d.js=20(#1290)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enable mermaid.js via https://squidfunk.github.io/mkdocs-material/reference/diagrams/#configuration --- mkdocs.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index ec6ed83e6d..cc95662a31 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -31,7 +31,11 @@ markdown_extensions: pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets - - pymdownx.superfences + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format - def_list - pymdownx.tasklist: custom_checkbox: true From e5ebe500c808ddd4107590fe6f646490dab0d4d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 11:00:22 +0000 Subject: [PATCH 029/694] :seedling: Bump mkdocs-material from 9.5.35 to 9.5.36 (#1300) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.35 to 9.5.36. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.35...9.5.36) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 378cb23457..3ba7e01fcf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ markdown2==2.5.0 MarkupSafe==2.1.5 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.5.35 +mkdocs-material==9.5.36 mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 From bc962f1a93a8d8241eae8f9cce9630f1fc927584 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:41:53 +0200 Subject: [PATCH 030/694] :seedling: Bump pymdown-extensions from 10.9 to 10.10.1 (#1299) Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.9 to 10.10.1. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.9...10.10.1) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3ba7e01fcf..ceb13458ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 Pygments==2.18.0 -pymdown-extensions==10.9 +pymdown-extensions==10.10.1 pyquery==2.0.1 python-dateutil==2.9.0.post0 PyYAML==6.0.2 From eea61cf2e8aec0f222dc3dadbd5b1fa506af1d55 Mon Sep 17 00:00:00 2001 From: Lalatendu Mohanty Date: Tue, 24 Sep 2024 14:55:07 -0400 Subject: [PATCH 031/694] Removing healthy status condition (#1304) Fixes #1295 Signed-off-by: Lalatendu Mohanty --- api/v1alpha1/clusterextension_types.go | 2 -- .../clusterextension_controller.go | 22 ------------------- .../clusterextension_controller_test.go | 12 ---------- 3 files changed, 36 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index 433f37859a..6628fe1ed0 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -414,7 +414,6 @@ const ( // TODO(user): add more Types, here and into init() TypeInstalled = "Installed" TypeResolved = "Resolved" - TypeHealthy = "Healthy" // TypeDeprecated is a rollup condition that is present when // any of the deprecated conditions are present. @@ -447,7 +446,6 @@ func init() { TypeChannelDeprecated, TypeBundleDeprecated, TypeUnpacked, - TypeHealthy, ) // TODO(user): add Reasons from above conditionsets.ConditionReasons = append(conditionsets.ConditionReasons, diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index 6e06f849d4..8cb8eeeb8e 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -316,35 +316,13 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp l.V(1).Info("watching managed objects") cache, err := r.Manager.Get(ctx, ext) if err != nil { - // If we fail to get the cache, set the Healthy condition to - // "Unknown". We can't know the health of resources we can't monitor - apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeHealthy, - Reason: ocv1alpha1.ReasonUnverifiable, - Status: metav1.ConditionUnknown, - Message: err.Error(), - ObservedGeneration: ext.Generation, - }) return ctrl.Result{}, err } if err := cache.Watch(ctx, r.controller, managedObjs...); err != nil { - // If we fail to establish watches, set the Healthy condition to - // "Unknown". We can't know the health of resources we can't monitor - apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeHealthy, - Reason: ocv1alpha1.ReasonUnverifiable, - Status: metav1.ConditionUnknown, - Message: err.Error(), - ObservedGeneration: ext.Generation, - }) return ctrl.Result{}, err } - // If we have successfully established the watches, remove the "Healthy" condition. - // It should be interpreted as "Unknown" when not present. - apimeta.RemoveStatusCondition(&ext.Status.Conditions, ocv1alpha1.TypeHealthy) - return ctrl.Result{}, nil } diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index 60be87deb5..0042e339e7 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -583,12 +583,6 @@ func TestClusterExtensionManagerFailed(t *testing.T) { require.Equal(t, metav1.ConditionTrue, installedCond.Status) require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) - t.Log("By checking the expected healthy conditions") - managedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeHealthy) - require.NotNil(t, managedCond) - require.Equal(t, metav1.ConditionUnknown, managedCond.Status) - require.Equal(t, ocv1alpha1.ReasonUnverifiable, managedCond.Reason) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } @@ -683,12 +677,6 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { require.Equal(t, metav1.ConditionTrue, installedCond.Status) require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) - t.Log("By checking the expected healthy conditions") - managedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeHealthy) - require.NotNil(t, managedCond) - require.Equal(t, metav1.ConditionUnknown, managedCond.Status) - require.Equal(t, ocv1alpha1.ReasonUnverifiable, managedCond.Reason) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } From d1a4dbfc78db4828962d7a430624b05f2b1c0057 Mon Sep 17 00:00:00 2001 From: Lalatendu Mohanty Date: Tue, 24 Sep 2024 23:40:31 -0400 Subject: [PATCH 032/694] :book: Adding OLM V1 architecture document (#1301) * Adding OLM V1 architecture document The documentation contains description of the components and a diagram. Removed the components.md file as the architecture doc contains the same information Fixes : [#1124](https://github.com/operator-framework/operator-controller/issues/1124) Signed-off-by: Lalatendu Mohanty * Update docs/drafts/architecture.md Co-authored-by: Per Goncalves da Silva * Update docs/drafts/architecture.md Co-authored-by: Per Goncalves da Silva * Update docs/drafts/architecture.md Co-authored-by: Per Goncalves da Silva * Update docs/drafts/architecture.md Co-authored-by: Per Goncalves da Silva * Addressing the review comments for the arch doc Signed-off-by: Lalatendu Mohanty --------- Signed-off-by: Lalatendu Mohanty Co-authored-by: Per Goncalves da Silva --- docs/components.md | 5 --- docs/drafts/architecture.md | 85 +++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 5 deletions(-) delete mode 100644 docs/components.md create mode 100644 docs/drafts/architecture.md diff --git a/docs/components.md b/docs/components.md deleted file mode 100644 index 160e72117a..0000000000 --- a/docs/components.md +++ /dev/null @@ -1,5 +0,0 @@ -OLM v1 is composed of various component projects: - -* [operator-controller](https://github.com/operator-framework/operator-controller): operator-controller is the central component of OLM v1, that consumes all of the components below to extend Kubernetes to allows users to install, and manage the lifecycle of other extensions - -* [catalogD](https://github.com/operator-framework/catalogd): Catalogd is a Kubernetes extension that unpacks [file-based catalog (FBC)](https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs) content that is packaged and shipped in container images, for consumption by clients on-clusters (unpacking from other sources, like git repos, OCI artifacts etc, are in the roadmap for catalogD). As component of the Operator Lifecycle Manager (OLM) v1 microservices architecture, catalogD hosts metadata for Kubernetes extensions packaged by the authors of the extensions, as a result helping customers discover installable content. diff --git a/docs/drafts/architecture.md b/docs/drafts/architecture.md new file mode 100644 index 0000000000..5be36f9af8 --- /dev/null +++ b/docs/drafts/architecture.md @@ -0,0 +1,85 @@ + +## OLM V1 Architecture + +This document describes the OLM v1 architecture. OLM v1 consists of two main components: + +* [operator-controller](https://github.com/operator-framework/operator-controller) +* [catalogD](https://github.com/operator-framework/catalogd) + +The diagram below illustrates the OLM v1 architecture and its components, and the following sections describe each of the components in detail. + +### Diagram + +```mermaid +flowchart TB + A(bundle) + B(registry repo) + C(catalog author) + D(bundle author) + E(file based catalog) + F(extension controller) + G(bundle cache) + H(catalog cache) + I(resolver) + J(catalog controller) + K(catalog content cache) + L(catalog http server) + + subgraph Image-registry + B + end + subgraph Cluster + subgraph Operator-controller + F-->G + F-->I + I-->H + end + subgraph Catalogd + J-->K + L<-->K + end + end + + F-->L + F-->B + J-->B + C -- creates --> E + E -- pushed to --> B + D -- creates --> A + A -- pushed to --> B +``` + +**Note**: The direction of the arrow indicates the active part of communication i.e. if arrow starts from A and points to B that means A consumes the information from B unless specifically mentioned. + +### Operator-controller: + +operator-controller is the central component of OLM v1. It is responsible: + * managing a cache of catalog metadata provided by catalogd through its HTTP server + * keeping the catalog metadata cache up-to-date with the current state of the catalogs + * locating the right `registry+v1` bundle, if any, that meet the constraints expressed in the `ClusterExtension` resource, such as package name, version range, channel, etc. given the current state of the cluster + * unpacking the bundle + * applying the bundle manifests: installing or updating the content. + + It has three main sub-components: + * Cluster Extension Controller: + * Queries the catalogd (catalogd HTTP Server) to get catalog information. + * Once received the catalog information is saved to catalog-cache. The cache will be updated automatically if a Catalog is noticed to have a different resolved image reference. + * Reaches out to the registry to download the bundle container images, saves it to the bundle cache, unpacks it and applies the bundle manifests to the cluster. + * It is also Responsible for figuring out which bundle to upgrade + * Resolver: + * Helps the cluster extension controller to filter the bundle reference after applying the user restrictions (e.g. name, priority etc) and returns the bundle reference to the extension controller. + * Bundle Cache: + * Bundle cache returns the cache for the bundle. If a cache does not already exist, a new one will be created. + +### Catalogd: + +Catalogd unpacks [file-based catalog (FBC)](https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs) content that is packaged and shipped in container images, for consumption by clients on-clusters (unpacking from other sources, like git repos, OCI artifacts etc, are in the roadmap for catalogD). It serves the extension metadata, provided by the extension authors, found in the FBC, making it possible for on-cluster clients to discover installable content. + +* Catalogd can be broken down in to three sub-components i.e. ClusterCatalog controller, catalogd http server, catalogd content cache. +* Catalog controller is responsible for pulling FBC based catalog images from registry and unpacking them into the catalog content cache. It is also responsible for reconciling the latest changes in the cluster catalog. +* Catalogd http server is responsible for serving catalog information to clients e.g. cluster extension controller. +* Catalogd content cache is maintained by the catalog controller and used by the catalogd http server to answer queries from clients. + + + + From 10510d9545d8b2a3764fef3e37b2b6aff243dabd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:23:49 +0200 Subject: [PATCH 033/694] :seedling: Bump mkdocs-material from 9.5.36 to 9.5.37 (#1308) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.36 to 9.5.37. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.36...9.5.37) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ceb13458ae..70e09ab0a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ markdown2==2.5.0 MarkupSafe==2.1.5 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.5.36 +mkdocs-material==9.5.37 mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 From 679e4ab4a6a96ef1cd8a82f05500cbe58af0b7f6 Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Wed, 25 Sep 2024 12:24:46 -0400 Subject: [PATCH 034/694] =?UTF-8?q?=E2=9C=A8=20add=20progressing=20conditi?= =?UTF-8?q?on=20(#1302)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add progressing condition Signed-off-by: everettraven * cleanup lingering healthy condition reason Signed-off-by: everettraven --------- Signed-off-by: everettraven --- api/v1alpha1/clusterextension_types.go | 23 +- .../clusterextension_controller.go | 35 ++- .../clusterextension_controller_test.go | 235 ++++-------------- internal/controllers/common_controller.go | 26 ++ .../controllers/common_controller_test.go | 66 +++++ internal/rukpak/source/unpacker.go | 9 - test/e2e/cluster_extension_install_test.go | 128 ++++++++++ 7 files changed, 305 insertions(+), 217 deletions(-) create mode 100644 internal/controllers/common_controller_test.go diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index 6628fe1ed0..cdb7a4afdc 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -22,12 +22,12 @@ import ( "github.com/operator-framework/operator-controller/internal/conditionsets" ) -var ( - ClusterExtensionKind = "ClusterExtension" -) +var ClusterExtensionKind = "ClusterExtension" -type UpgradeConstraintPolicy string -type CRDUpgradeSafetyPolicy string +type ( + UpgradeConstraintPolicy string + CRDUpgradeSafetyPolicy string +) const ( // The extension will only upgrade if the new version satisfies @@ -412,8 +412,9 @@ type CRDUpgradeSafetyPreflightConfig struct { const ( // TODO(user): add more Types, here and into init() - TypeInstalled = "Installed" - TypeResolved = "Resolved" + TypeInstalled = "Installed" + TypeResolved = "Resolved" + TypeProgressing = "Progressing" // TypeDeprecated is a rollup condition that is present when // any of the deprecated conditions are present. @@ -426,12 +427,12 @@ const ( ReasonSuccess = "Succeeded" ReasonDeprecated = "Deprecated" ReasonFailed = "Failed" + ReasonBlocked = "Blocked" + ReasonRetrying = "Retrying" ReasonErrorGettingClient = "ErrorGettingClient" ReasonErrorGettingReleaseState = "ErrorGettingReleaseState" - ReasonUnverifiable = "Unverifiable" - CRDUpgradeSafetyPolicyEnabled CRDUpgradeSafetyPolicy = "Enabled" CRDUpgradeSafetyPolicyDisabled CRDUpgradeSafetyPolicy = "Disabled" ) @@ -446,6 +447,7 @@ func init() { TypeChannelDeprecated, TypeBundleDeprecated, TypeUnpacked, + TypeProgressing, ) // TODO(user): add Reasons from above conditionsets.ConditionReasons = append(conditionsets.ConditionReasons, @@ -454,7 +456,8 @@ func init() { ReasonFailed, ReasonErrorGettingClient, ReasonErrorGettingReleaseState, - ReasonUnverifiable, + ReasonBlocked, + ReasonRetrying, ) } diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index 8cb8eeeb8e..0e7180d1d7 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -201,6 +201,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp setInstallStatus(ext, nil) setResolutionStatus(ext, nil) setResolvedStatusConditionFailed(ext, err.Error()) + setStatusProgressing(ext, err) ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, err.Error()) return ctrl.Result{}, err } @@ -216,6 +217,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp setInstallStatus(ext, nil) // TODO: use Installed=Unknown setInstalledStatusConditionFailed(ext, err.Error()) + setStatusProgressing(ext, err) return ctrl.Result{}, err } @@ -227,6 +229,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp setInstallStatus(ext, nil) setResolutionStatus(ext, nil) setResolvedStatusConditionFailed(ext, err.Error()) + setStatusProgressing(ext, err) ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, err.Error()) return ctrl.Result{}, err } @@ -247,8 +250,9 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp // all catalogs? SetDeprecationStatus(ext, resolvedBundle.Name, resolvedDeprecation) + resolvedBundleMetadata := bundleutil.MetadataFor(resolvedBundle.Name, *resolvedBundleVersion) resStatus := &ocv1alpha1.ClusterExtensionResolutionStatus{ - Bundle: bundleutil.MetadataFor(resolvedBundle.Name, *resolvedBundleVersion), + Bundle: resolvedBundleMetadata, } setResolutionStatus(ext, resStatus) setResolvedStatusConditionSuccess(ext, fmt.Sprintf("resolved to %q", resolvedBundle.Image)) @@ -264,20 +268,18 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp unpackResult, err := r.Unpacker.Unpack(ctx, bundleSource) if err != nil { setStatusUnpackFailed(ext, err.Error()) + // Wrap the error passed to this with the resolution information until we have successfully + // installed since we intend for the progressing condition to replace the resolved condition + // and will be removing the .status.resolution field from the ClusterExtension status API + setStatusProgressing(ext, wrapErrorWithResolutionInfo(resolvedBundleMetadata, err)) return ctrl.Result{}, err } switch unpackResult.State { - case rukpaksource.StatePending: - setStatusUnpackFailed(ext, unpackResult.Message) - ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, "unpack pending") - return ctrl.Result{}, nil case rukpaksource.StateUnpacked: setStatusUnpacked(ext, unpackResult.Message) default: - setStatusUnpackFailed(ext, "unexpected unpack status") - // We previously exit with a failed status if error is not nil. - return ctrl.Result{}, fmt.Errorf("unexpected unpack status: %v", unpackResult.Message) + panic(fmt.Sprintf("unexpected unpack state %q", unpackResult.State)) } objLbls := map[string]string{ @@ -304,11 +306,12 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp managedObjs, _, err := r.Applier.Apply(ctx, unpackResult.Bundle, ext, objLbls, storeLbls) if err != nil { setInstalledStatusConditionFailed(ext, err.Error()) + setStatusProgressing(ext, wrapErrorWithResolutionInfo(resolvedBundleMetadata, err)) return ctrl.Result{}, err } installStatus := &ocv1alpha1.ClusterExtensionInstallStatus{ - Bundle: bundleutil.MetadataFor(resolvedBundle.Name, *resolvedBundleVersion), + Bundle: resolvedBundleMetadata, } setInstallStatus(ext, installStatus) setInstalledStatusConditionSuccess(ext, fmt.Sprintf("Installed bundle %s successfully", resolvedBundle.Image)) @@ -316,13 +319,23 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp l.V(1).Info("watching managed objects") cache, err := r.Manager.Get(ctx, ext) if err != nil { + // No need to wrap error with resolution information here (or beyond) since the + // bundle was successfully installed and the information will be present in + // the .status.installed field + setStatusProgressing(ext, err) return ctrl.Result{}, err } if err := cache.Watch(ctx, r.controller, managedObjs...); err != nil { + setStatusProgressing(ext, err) return ctrl.Result{}, err } + // If we made it here, we have successfully reconciled the ClusterExtension + // and have reached the desired state. Since the Progressing status should reflect + // our progress towards the desired state, we also set it when we have reached + // the desired state by providing a nil error value. + setStatusProgressing(ext, nil) return ctrl.Result{}, nil } @@ -438,6 +451,10 @@ func (r *ClusterExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error { return nil } +func wrapErrorWithResolutionInfo(resolved ocv1alpha1.BundleMetadata, err error) error { + return fmt.Errorf("%w for resolved bundle %q with version %q", err, resolved.Name, resolved.Version) +} + // Generate reconcile requests for all cluster extensions affected by a catalog change func clusterExtensionRequestsForCatalog(c client.Reader, logger logr.Logger) crhandler.MapFunc { return func(ctx context.Context, _ client.Object) []reconcile.Request { diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index 0042e339e7..e3f613bf00 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -89,89 +89,17 @@ func TestClusterExtensionResolutionFails(t *testing.T) { require.Equal(t, ocv1alpha1.ReasonFailed, cond.Reason) require.Equal(t, fmt.Sprintf("no package %q found", pkgName), cond.Message) - verifyInvariants(ctx, t, reconciler.Client, clusterExtension) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) -} - -func TestClusterExtensionResolutionSucceeds(t *testing.T) { - cl, reconciler := newClientAndReconciler(t) - reconciler.Unpacker = &MockUnpacker{ - result: &source.Result{ - State: source.StatePending, - }, - } - - ctx := context.Background() - extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - - t.Log("When the cluster extension specifies a channel with version that exist") - t.Log("By initializing cluster state") - pkgName := "prometheus" - pkgVer := "1.0.0" - pkgChan := "beta" - namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) - serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - - clusterExtension := &ocv1alpha1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ - SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ - PackageName: pkgName, - Version: pkgVer, - Channels: []string{pkgChan}, - }, - }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, - }, - }, - } - err := cl.Create(ctx, clusterExtension) - require.NoError(t, err) - - t.Log("It sets resolution success status") - t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { - v := bsemver.MustParse("1.0.0") - return &declcfg.Bundle{ - Name: "prometheus.v1.0.0", - Package: "prometheus", - Image: "quay.io/operatorhubio/prometheus@fake1.0.0", - }, &v, nil, nil - }) - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) - require.Equal(t, ctrl.Result{}, res) - require.NoError(t, err) - - t.Log("By fetching updated cluster extension after reconcile") - require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) - - t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) - require.Empty(t, clusterExtension.Status.Install) - - t.Log("By checking the expected conditions") - resolvedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - require.NotNil(t, resolvedCond) - require.Equal(t, metav1.ConditionTrue, resolvedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) - require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - - t.Log("By checking the expected unpacked conditions") - unpackedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - require.NotNil(t, unpackedCond) - require.Equal(t, metav1.ConditionFalse, unpackedCond.Status) - require.Equal(t, ocv1alpha1.ReasonFailed, unpackedCond.Reason) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, cond) + require.Equal(t, metav1.ConditionTrue, cond.Status) + require.Equal(t, ocv1alpha1.ReasonRetrying, cond.Reason) + require.Equal(t, fmt.Sprintf("no package %q found", pkgName), cond.Message) + verifyInvariants(ctx, t, reconciler.Client, clusterExtension) require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } -func TestClusterExtensionUnpackFails(t *testing.T) { +func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { cl, reconciler := newClientAndReconciler(t) reconciler.Unpacker = &MockUnpacker{ err: errors.New("unpack failure"), @@ -228,7 +156,8 @@ func TestClusterExtensionUnpackFails(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) + expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} + require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle) require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected conditions") @@ -244,6 +173,12 @@ func TestClusterExtensionUnpackFails(t *testing.T) { require.Equal(t, metav1.ConditionFalse, unpackedCond.Status) require.Equal(t, ocv1alpha1.ReasonFailed, unpackedCond.Reason) + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, progressingCond) + require.Equal(t, metav1.ConditionTrue, progressingCond.Status) + require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) + require.Contains(t, progressingCond.Message, fmt.Sprintf("for resolved bundle %q with version %q", expectedBundleMetadata.Name, expectedBundleMetadata.Version)) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } @@ -298,116 +233,12 @@ func TestClusterExtensionUnpackUnexpectedState(t *testing.T) { Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, &v, nil, nil }) - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) - require.Equal(t, ctrl.Result{}, res) - require.Error(t, err) - - t.Log("By fetching updated cluster extension after reconcile") - require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) - - t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) - require.Empty(t, clusterExtension.Status.Install) - - t.Log("By checking the expected conditions") - resolvedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - require.NotNil(t, resolvedCond) - require.Equal(t, metav1.ConditionTrue, resolvedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) - require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - - t.Log("By checking the expected unpacked conditions") - unpackedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - require.NotNil(t, unpackedCond) - require.Equal(t, metav1.ConditionFalse, unpackedCond.Status) - require.Equal(t, ocv1alpha1.ReasonFailed, unpackedCond.Reason) - - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) -} - -func TestClusterExtensionUnpackSucceeds(t *testing.T) { - cl, reconciler := newClientAndReconciler(t) - reconciler.Unpacker = &MockUnpacker{ - result: &source.Result{ - State: source.StateUnpacked, - Bundle: fstest.MapFS{}, - }, - } - - ctx := context.Background() - extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - - t.Log("When the cluster extension specifies a channel with version that exist") - t.Log("By initializing cluster state") - pkgName := "prometheus" - pkgVer := "1.0.0" - pkgChan := "beta" - namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) - serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - - clusterExtension := &ocv1alpha1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ - SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ - PackageName: pkgName, - Version: pkgVer, - Channels: []string{pkgChan}, - }, - }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, - }, - }, - } - err := cl.Create(ctx, clusterExtension) - require.NoError(t, err) - - t.Log("It sets resolution success status") - t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { - v := bsemver.MustParse("1.0.0") - return &declcfg.Bundle{ - Name: "prometheus.v1.0.0", - Package: "prometheus", - Image: "quay.io/operatorhubio/prometheus@fake1.0.0", - }, &v, nil, nil - }) - reconciler.Applier = &MockApplier{ - err: errors.New("apply failure"), - } - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) - require.Equal(t, ctrl.Result{}, res) - require.Error(t, err) - - t.Log("By fetching updated cluster extension after reconcile") - require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) - - t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) - require.Empty(t, clusterExtension.Status.Install) - - t.Log("By checking the expected resolution conditions") - resolvedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - require.NotNil(t, resolvedCond) - require.Equal(t, metav1.ConditionTrue, resolvedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) - require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - - t.Log("By checking the expected unpacked conditions") - unpackedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - require.NotNil(t, unpackedCond) - require.Equal(t, metav1.ConditionTrue, unpackedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, unpackedCond.Reason) - - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.Panics(t, func() { + _, _ = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) + }, "reconciliation should panic on unknown unpack state") } -func TestClusterExtensionInstallationFailedApplierFails(t *testing.T) { +func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) { cl, reconciler := newClientAndReconciler(t) reconciler.Unpacker = &MockUnpacker{ result: &source.Result{ @@ -470,7 +301,8 @@ func TestClusterExtensionInstallationFailedApplierFails(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) + expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} + require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle) require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected resolution conditions") @@ -492,6 +324,13 @@ func TestClusterExtensionInstallationFailedApplierFails(t *testing.T) { require.Equal(t, metav1.ConditionFalse, installedCond.Status) require.Equal(t, ocv1alpha1.ReasonFailed, installedCond.Reason) + t.Log("By checking the expected progressing conditions") + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, progressingCond) + require.Equal(t, metav1.ConditionTrue, progressingCond.Status) + require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) + require.Contains(t, progressingCond.Message, fmt.Sprintf("for resolved bundle %q with version %q", expectedBundleMetadata.Name, expectedBundleMetadata.Version)) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } @@ -583,6 +422,12 @@ func TestClusterExtensionManagerFailed(t *testing.T) { require.Equal(t, metav1.ConditionTrue, installedCond.Status) require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) + t.Log("By checking the expected progressing conditions") + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, progressingCond) + require.Equal(t, metav1.ConditionTrue, progressingCond.Status) + require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } @@ -677,6 +522,12 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { require.Equal(t, metav1.ConditionTrue, installedCond.Status) require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) + t.Log("By checking the expected progressing conditions") + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, progressingCond) + require.Equal(t, metav1.ConditionTrue, progressingCond.Status) + require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } @@ -768,6 +619,12 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { require.Equal(t, metav1.ConditionTrue, installedCond.Status) require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) + t.Log("By checking the expected progressing conditions") + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, progressingCond) + require.Equal(t, metav1.ConditionFalse, progressingCond.Status) + require.Equal(t, ocv1alpha1.ReasonSuccess, progressingCond.Reason) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index 8beeb3e31f..d03644d760 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -17,8 +17,11 @@ limitations under the License. package controllers import ( + "errors" + apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/reconcile" ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) @@ -98,3 +101,26 @@ func setResolutionStatus(ext *ocv1alpha1.ClusterExtension, resStatus *ocv1alpha1 func setInstallStatus(ext *ocv1alpha1.ClusterExtension, installStatus *ocv1alpha1.ClusterExtensionInstallStatus) { ext.Status.Install = installStatus } + +func setStatusProgressing(ext *ocv1alpha1.ClusterExtension, err error) { + progressingCond := metav1.Condition{ + Type: ocv1alpha1.TypeProgressing, + Status: metav1.ConditionFalse, + Reason: ocv1alpha1.ReasonSuccess, + Message: "desired state reached", + ObservedGeneration: ext.GetGeneration(), + } + + if err != nil { + progressingCond.Status = metav1.ConditionTrue + progressingCond.Reason = ocv1alpha1.ReasonRetrying + progressingCond.Message = err.Error() + } + + if errors.Is(err, reconcile.TerminalError(nil)) { + progressingCond.Status = metav1.ConditionFalse + progressingCond.Reason = ocv1alpha1.ReasonBlocked + } + + apimeta.SetStatusCondition(&ext.Status.Conditions, progressingCond) +} diff --git a/internal/controllers/common_controller_test.go b/internal/controllers/common_controller_test.go new file mode 100644 index 0000000000..8715464d63 --- /dev/null +++ b/internal/controllers/common_controller_test.go @@ -0,0 +1,66 @@ +package controllers + +import ( + "errors" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" +) + +func TestSetStatusProgressing(t *testing.T) { + for _, tc := range []struct { + name string + err error + clusterExtension *ocv1alpha1.ClusterExtension + expected metav1.Condition + }{ + { + name: "non-nil ClusterExtension, nil error, Progressing condition has status False with reason Success", + err: nil, + clusterExtension: &ocv1alpha1.ClusterExtension{}, + expected: metav1.Condition{ + Type: ocv1alpha1.TypeProgressing, + Status: metav1.ConditionFalse, + Reason: ocv1alpha1.ReasonSuccess, + Message: "desired state reached", + }, + }, + { + name: "non-nil ClusterExtension, non-terminal error, Progressing condition has status True with reason Retrying", + err: errors.New("boom"), + clusterExtension: &ocv1alpha1.ClusterExtension{}, + expected: metav1.Condition{ + Type: ocv1alpha1.TypeProgressing, + Status: metav1.ConditionTrue, + Reason: ocv1alpha1.ReasonRetrying, + Message: "boom", + }, + }, + { + name: "non-nil ClusterExtension, terminal error, Progressing condition has status False with reason Blocked", + err: reconcile.TerminalError(errors.New("boom")), + clusterExtension: &ocv1alpha1.ClusterExtension{}, + expected: metav1.Condition{ + Type: ocv1alpha1.TypeProgressing, + Status: metav1.ConditionFalse, + Reason: ocv1alpha1.ReasonBlocked, + Message: "terminal error: boom", + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + setStatusProgressing(tc.clusterExtension, tc.err) + progressingCond := meta.FindStatusCondition(tc.clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, progressingCond, "progressing condition should be set but was not") + diff := cmp.Diff(*progressingCond, tc.expected, cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime", "ObservedGeneration")) + require.Empty(t, diff, "difference between actual and expected Progressing conditions") + }) + } +} diff --git a/internal/rukpak/source/unpacker.go b/internal/rukpak/source/unpacker.go index f56de0010a..013f96989e 100644 --- a/internal/rukpak/source/unpacker.go +++ b/internal/rukpak/source/unpacker.go @@ -59,15 +59,6 @@ type Result struct { type State string const ( - // StatePending conveys that a request for unpacking a bundle has been - // acknowledged, but not yet started. - StatePending State = "Pending" - - // StateUnpacking conveys that the source is currently unpacking a bundle. - // This state should be used when the bundle contents are being downloaded - // and processed. - StateUnpacking State = "Unpacking" - // StateUnpacked conveys that the bundle has been successfully unpacked. StateUnpacked State = "Unpacked" ) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 58e1b30112..cff9f1b6a3 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -296,6 +296,17 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { assert.Regexp(ct, "^unpacked .* successfully", cond.Message) }, pollDuration, pollInterval) + t.Log("By eventually reporting no longer progressing") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + }, pollDuration, pollInterval) + t.Log("By eventually installing the package successfully") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) @@ -349,6 +360,18 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [operatorhubio test-catalog]") assert.Nil(ct, clusterExtension.Status.Resolution) }, pollDuration, pollInterval) + + t.Log("By eventually reporting Progressing == True and Reason Retrying") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) + assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [operatorhubio test-catalog]") + }, pollDuration, pollInterval) } func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { @@ -386,6 +409,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { } assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") + assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.0.0", @@ -400,6 +424,13 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { }}, clusterExtension.Status.Install, ) + + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) t.Log("It does not allow to upgrade the ClusterExtension to a non-successor version") @@ -418,6 +449,17 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"prometheus\" matching version \"1.2.0\"", cond.Message) assert.Empty(ct, clusterExtension.Status.Resolution) }, pollDuration, pollInterval) + + t.Log("By eventually reporting Progressing == True and Reason Retrying") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) + assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"prometheus\" matching version \"1.2.0\"", cond.Message) + }, pollDuration, pollInterval) } func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { @@ -454,6 +496,14 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { } assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") + + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.0.0", @@ -478,6 +528,14 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { } assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") + + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.2.0", @@ -521,6 +579,14 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { } assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") + + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.0.0", @@ -544,6 +610,14 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { } assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") + + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.0.1", @@ -598,6 +672,14 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") + + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.2.0", @@ -632,6 +714,14 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") + + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.2.0.0", @@ -698,6 +788,14 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") + + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.2.0", @@ -732,6 +830,14 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") + + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.2.0.0", @@ -874,6 +980,17 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes assert.Regexp(ct, "^unpacked .* successfully", cond.Message) }, pollDuration, pollInterval) + t.Log("By eventually reporting Progressing == True with Reason Retrying") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) + }, pollDuration, pollInterval) + t.Log("By eventually failing to install the package successfully due to insufficient ServiceAccount permissions") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) @@ -905,6 +1022,17 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes assert.Contains(ct, cond.Message, "Installed bundle") assert.NotEmpty(ct, clusterExtension.Status.Install) }, pollDuration, pollInterval) + + t.Log("By eventually reporting Progressing == False with Reason Success") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + }, pollDuration, pollInterval) } // getArtifactsOutput gets all the artifacts from the test run and saves them to the artifact path. From 40ada4a7ad7b27d426251d791074a6882bea66ee Mon Sep 17 00:00:00 2001 From: Sid Kattoju <83437591+skattoju@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:07:25 -0400 Subject: [PATCH 035/694] remove unpacked status (#1309) --- api/v1alpha1/clusterextension_types.go | 2 -- .../clusterextension_controller.go | 6 +--- .../clusterextension_controller_test.go | 33 ++----------------- internal/controllers/common_controller.go | 24 -------------- test/e2e/cluster_extension_install_test.go | 24 -------------- 5 files changed, 4 insertions(+), 85 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index cdb7a4afdc..829cb5008e 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -422,7 +422,6 @@ const ( TypePackageDeprecated = "PackageDeprecated" TypeChannelDeprecated = "ChannelDeprecated" TypeBundleDeprecated = "BundleDeprecated" - TypeUnpacked = "Unpacked" ReasonSuccess = "Succeeded" ReasonDeprecated = "Deprecated" @@ -446,7 +445,6 @@ func init() { TypePackageDeprecated, TypeChannelDeprecated, TypeBundleDeprecated, - TypeUnpacked, TypeProgressing, ) // TODO(user): add Reasons from above diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index 0e7180d1d7..cdbe1ade2d 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -267,7 +267,6 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp l.V(1).Info("unpacking resolved bundle") unpackResult, err := r.Unpacker.Unpack(ctx, bundleSource) if err != nil { - setStatusUnpackFailed(ext, err.Error()) // Wrap the error passed to this with the resolution information until we have successfully // installed since we intend for the progressing condition to replace the resolved condition // and will be removing the .status.resolution field from the ClusterExtension status API @@ -275,10 +274,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp return ctrl.Result{}, err } - switch unpackResult.State { - case rukpaksource.StateUnpacked: - setStatusUnpacked(ext, unpackResult.Message) - default: + if unpackResult.State != rukpaksource.StateUnpacked { panic(fmt.Sprintf("unexpected unpack state %q", unpackResult.State)) } diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index e3f613bf00..d9603fab02 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -167,12 +167,6 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - t.Log("By checking the expected unpacked conditions") - unpackedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - require.NotNil(t, unpackedCond) - require.Equal(t, metav1.ConditionFalse, unpackedCond.Status) - require.Equal(t, ocv1alpha1.ReasonFailed, unpackedCond.Reason) - progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) require.NotNil(t, progressingCond) require.Equal(t, metav1.ConditionTrue, progressingCond.Status) @@ -233,9 +227,12 @@ func TestClusterExtensionUnpackUnexpectedState(t *testing.T) { Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, &v, nil, nil }) + require.Panics(t, func() { _, _ = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) }, "reconciliation should panic on unknown unpack state") + + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) { @@ -312,12 +309,6 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - t.Log("By checking the expected unpacked conditions") - unpackedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - require.NotNil(t, unpackedCond) - require.Equal(t, metav1.ConditionTrue, unpackedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, unpackedCond.Reason) - t.Log("By checking the expected installed conditions") installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) @@ -410,12 +401,6 @@ func TestClusterExtensionManagerFailed(t *testing.T) { require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - t.Log("By checking the expected unpacked conditions") - unpackedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - require.NotNil(t, unpackedCond) - require.Equal(t, metav1.ConditionTrue, unpackedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, unpackedCond.Reason) - t.Log("By checking the expected installed conditions") installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) @@ -510,12 +495,6 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - t.Log("By checking the expected unpacked conditions") - unpackedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - require.NotNil(t, unpackedCond) - require.Equal(t, metav1.ConditionTrue, unpackedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, unpackedCond.Reason) - t.Log("By checking the expected installed conditions") installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) @@ -607,12 +586,6 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - t.Log("By checking the expected unpacked conditions") - unpackedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - require.NotNil(t, unpackedCond) - require.Equal(t, metav1.ConditionTrue, unpackedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, unpackedCond.Reason) - t.Log("By checking the expected installed conditions") installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index d03644d760..66a2d1e24b 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -70,30 +70,6 @@ func setInstalledStatusConditionFailed(ext *ocv1alpha1.ClusterExtension, message }) } -func setStatusUnpackFailed(ext *ocv1alpha1.ClusterExtension, message string) { - setInstallStatus(ext, nil) - apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeUnpacked, - Status: metav1.ConditionFalse, - Reason: ocv1alpha1.ReasonFailed, - Message: message, - ObservedGeneration: ext.GetGeneration(), - }) -} - -func setStatusUnpacked(ext *ocv1alpha1.ClusterExtension, message string) { - if message == "" { - message = "unpack successful" - } - apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeUnpacked, - Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonSuccess, - Message: message, - ObservedGeneration: ext.GetGeneration(), - }) -} - func setResolutionStatus(ext *ocv1alpha1.ClusterExtension, resStatus *ocv1alpha1.ClusterExtensionResolutionStatus) { ext.Status.Resolution = resStatus } diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index cff9f1b6a3..8dc0167043 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -284,18 +284,6 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { ) }, pollDuration, pollInterval) - t.Log("By eventually reporting a successful unpacked") - require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Regexp(ct, "^unpacked .* successfully", cond.Message) - }, pollDuration, pollInterval) - t.Log("By eventually reporting no longer progressing") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) @@ -968,18 +956,6 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes ) }, pollDuration, pollInterval) - t.Log("By eventually reporting a successful unpacked") - require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeUnpacked) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Regexp(ct, "^unpacked .* successfully", cond.Message) - }, pollDuration, pollInterval) - t.Log("By eventually reporting Progressing == True with Reason Retrying") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) From d9cf2ed87d5e349bbf8805c14198826345408314 Mon Sep 17 00:00:00 2001 From: Anik Date: Thu, 26 Sep 2024 22:00:15 +0530 Subject: [PATCH 036/694] =?UTF-8?q?=E2=9C=A8=20Use=20creds=20if=20present?= =?UTF-8?q?=20for=20pulling=20bundle=20images=20(#1303)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/manager/main.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 55951ca836..fce0dd8ade 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -26,6 +26,7 @@ import ( "time" "github.com/containers/image/v5/types" + "github.com/go-logr/logr" "github.com/spf13/pflag" "go.uber.org/zap/zapcore" apiextensionsv1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" @@ -67,6 +68,8 @@ var ( defaultSystemNamespace = "olmv1-system" ) +const authFilePath = "/etc/operator-controller/auth.json" + // podNamespace checks whether the controller is running in a Pod vs. // being run locally by inspecting the namespace file that gets mounted // automatically for Pods at runtime. If that file doesn't exist, then @@ -201,6 +204,7 @@ func main() { SourceContext: &types.SystemContext{ DockerCertPath: caCertDir, OCICertPath: caCertDir, + AuthFilePath: authFilePathIfPresent(setupLog), }, } @@ -311,3 +315,15 @@ type finalizerFunc func(ctx context.Context, obj client.Object) (crfinalizer.Res func (f finalizerFunc) Finalize(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { return f(ctx, obj) } + +func authFilePathIfPresent(logger logr.Logger) string { + _, err := os.Stat(authFilePath) + if os.IsNotExist(err) { + return "" + } + if err != nil { + logger.Error(err, "could not stat auth file path", "path", authFilePath) + os.Exit(1) + } + return authFilePath +} From 8699d2535ed5d8a214e76e91fac13d8dbf595b8e Mon Sep 17 00:00:00 2001 From: Yash Devan Oza Date: Thu, 26 Sep 2024 14:22:31 -0400 Subject: [PATCH 037/694] [WIP] Remove the Resolved status condition (#1312) Signed-off-by: yashoza19 --- api/v1alpha1/clusterextension_types.go | 2 - .../clusterextension_controller.go | 3 - .../clusterextension_controller_test.go | 42 +------ internal/controllers/common_controller.go | 22 ---- test/e2e/cluster_extension_install_test.go | 112 ++---------------- 5 files changed, 10 insertions(+), 171 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index 829cb5008e..25c0a32b21 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -413,7 +413,6 @@ type CRDUpgradeSafetyPreflightConfig struct { const ( // TODO(user): add more Types, here and into init() TypeInstalled = "Installed" - TypeResolved = "Resolved" TypeProgressing = "Progressing" // TypeDeprecated is a rollup condition that is present when @@ -440,7 +439,6 @@ func init() { // TODO(user): add Types from above conditionsets.ConditionTypes = append(conditionsets.ConditionTypes, TypeInstalled, - TypeResolved, TypeDeprecated, TypePackageDeprecated, TypeChannelDeprecated, diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index cdbe1ade2d..399620cd48 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -200,7 +200,6 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp // it is properly labeled with its observed generation. setInstallStatus(ext, nil) setResolutionStatus(ext, nil) - setResolvedStatusConditionFailed(ext, err.Error()) setStatusProgressing(ext, err) ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, err.Error()) return ctrl.Result{}, err @@ -228,7 +227,6 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp // Note: We don't distinguish between resolution-specific errors and generic errors setInstallStatus(ext, nil) setResolutionStatus(ext, nil) - setResolvedStatusConditionFailed(ext, err.Error()) setStatusProgressing(ext, err) ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, err.Error()) return ctrl.Result{}, err @@ -255,7 +253,6 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp Bundle: resolvedBundleMetadata, } setResolutionStatus(ext, resStatus) - setResolvedStatusConditionSuccess(ext, fmt.Sprintf("resolved to %q", resolvedBundle.Image)) bundleSource := &rukpaksource.BundleSource{ Name: ext.GetName(), diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index d9603fab02..d38404b9c2 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -83,13 +83,7 @@ func TestClusterExtensionResolutionFails(t *testing.T) { require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - require.NotNil(t, cond) - require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, ocv1alpha1.ReasonFailed, cond.Reason) - require.Equal(t, fmt.Sprintf("no package %q found", pkgName), cond.Message) - - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) require.Equal(t, ocv1alpha1.ReasonRetrying, cond.Reason) @@ -161,12 +155,6 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected conditions") - resolvedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - require.NotNil(t, resolvedCond) - require.Equal(t, metav1.ConditionTrue, resolvedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) - require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) require.NotNil(t, progressingCond) require.Equal(t, metav1.ConditionTrue, progressingCond.Status) @@ -302,13 +290,6 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle) require.Empty(t, clusterExtension.Status.Install) - t.Log("By checking the expected resolution conditions") - resolvedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - require.NotNil(t, resolvedCond) - require.Equal(t, metav1.ConditionTrue, resolvedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) - require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - t.Log("By checking the expected installed conditions") installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) @@ -394,13 +375,6 @@ func TestClusterExtensionManagerFailed(t *testing.T) { require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) - t.Log("By checking the expected resolution conditions") - resolvedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - require.NotNil(t, resolvedCond) - require.Equal(t, metav1.ConditionTrue, resolvedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) - require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - t.Log("By checking the expected installed conditions") installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) @@ -488,13 +462,6 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) - t.Log("By checking the expected resolution conditions") - resolvedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - require.NotNil(t, resolvedCond) - require.Equal(t, metav1.ConditionTrue, resolvedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) - require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - t.Log("By checking the expected installed conditions") installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) @@ -579,13 +546,6 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) - t.Log("By checking the expected resolution conditions") - resolvedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - require.NotNil(t, resolvedCond) - require.Equal(t, metav1.ConditionTrue, resolvedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, resolvedCond.Reason) - require.Equal(t, "resolved to \"quay.io/operatorhubio/prometheus@fake1.0.0\"", resolvedCond.Message) - t.Log("By checking the expected installed conditions") installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index 66a2d1e24b..1ef5a18cfa 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -26,28 +26,6 @@ import ( ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) -// setResolvedStatusConditionSuccess sets the resolved status condition to success. -func setResolvedStatusConditionSuccess(ext *ocv1alpha1.ClusterExtension, message string) { - apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeResolved, - Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonSuccess, - Message: message, - ObservedGeneration: ext.GetGeneration(), - }) -} - -// setResolvedStatusConditionFailed sets the resolved status condition to failed. -func setResolvedStatusConditionFailed(ext *ocv1alpha1.ClusterExtension, message string) { - apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeResolved, - Status: metav1.ConditionFalse, - Reason: ocv1alpha1.ReasonFailed, - Message: message, - ObservedGeneration: ext.GetGeneration(), - }) -} - // setInstalledStatusConditionSuccess sets the installed status condition to success. func setInstalledStatusConditionSuccess(ext *ocv1alpha1.ClusterExtension, message string) { apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 8dc0167043..351f777f51 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -268,13 +268,6 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: fmt.Sprintf("%s-operator.1.2.0", tc.packageName), @@ -339,13 +332,6 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { t.Log("By eventually reporting a failed resolution with multiple bundles") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonFailed, cond.Reason) - assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [operatorhubio test-catalog]") assert.Nil(ct, clusterExtension.Status.Resolution) }, pollDuration, pollInterval) @@ -391,13 +377,6 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { t.Log("By eventually reporting a successful installation") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.0.0", @@ -413,7 +392,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { clusterExtension.Status.Install, ) - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if !assert.NotNil(ct, cond) { return } @@ -429,12 +408,6 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { t.Log("By eventually reporting an unsatisfiable resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, ocv1alpha1.ReasonFailed, cond.Reason) - assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"prometheus\" matching version \"1.2.0\"", cond.Message) assert.Empty(ct, clusterExtension.Status.Resolution) }, pollDuration, pollInterval) @@ -478,14 +451,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { t.Log("By eventually reporting a successful resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if !assert.NotNil(ct, cond) { return } @@ -510,14 +476,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { t.Log("By eventually reporting a satisfiable resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if !assert.NotNil(ct, cond) { return } @@ -561,14 +520,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { t.Log("By eventually reporting a successful resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if !assert.NotNil(ct, cond) { return } @@ -592,14 +544,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if !assert.NotNil(ct, cond) { return } @@ -653,15 +598,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { t.Log("By reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if !assert.NotNil(ct, cond) { return } @@ -695,15 +632,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if !assert.NotNil(ct, cond) { return } @@ -769,15 +698,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { t.Log("By reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if !assert.NotNil(ct, cond) { return } @@ -811,15 +732,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") - - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if !assert.NotNil(ct, cond) { return } @@ -940,13 +853,6 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "resolved to") assert.Equal(ct, &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.2.0", From edf108c654c804d7d8c5f9a3b3441961ca1d001a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:07:57 +0200 Subject: [PATCH 038/694] :seedling: Bump pymdown-extensions from 10.10.1 to 10.10.2 (#1310) Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.10.1 to 10.10.2. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.10.1...10.10.2) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 70e09ab0a8..5d71d49a13 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 Pygments==2.18.0 -pymdown-extensions==10.10.1 +pymdown-extensions==10.10.2 pyquery==2.0.1 python-dateutil==2.9.0.post0 PyYAML==6.0.2 From 360f892b2d84ffe150544cda550d2cbc06d14f4f Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Fri, 27 Sep 2024 08:37:51 -0400 Subject: [PATCH 039/694] main.go: improve logging for configuration of global auth (#1316) Signed-off-by: Joe Lanford --- cmd/manager/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index fce0dd8ade..b9c855a7a1 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -319,11 +319,13 @@ func (f finalizerFunc) Finalize(ctx context.Context, obj client.Object) (crfinal func authFilePathIfPresent(logger logr.Logger) string { _, err := os.Stat(authFilePath) if os.IsNotExist(err) { + logger.Info("auth file not found, skipping configuration of global auth file", "path", authFilePath) return "" } if err != nil { - logger.Error(err, "could not stat auth file path", "path", authFilePath) + logger.Error(err, "unable to access auth file path", "path", authFilePath) os.Exit(1) } + logger.Info("auth file found, configuring globally for image registry interactions", "path", authFilePath) return authFilePath } From c5686d1def66b350635ee2a3fdacf43d32d55069 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Fri, 27 Sep 2024 15:10:15 -0400 Subject: [PATCH 040/694] main.go: switch to klog-based logger (#1317) Signed-off-by: Joe Lanford --- cmd/manager/main.go | 14 ++++++-------- config/base/manager/manager.yaml | 10 +++++----- go.mod | 5 +---- .../controllers/clusterextension_controller.go | 16 ++++++++-------- test/upgrade-e2e/post_upgrade_test.go | 2 +- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index b9c855a7a1..d5da75c322 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -28,18 +28,18 @@ import ( "github.com/containers/image/v5/types" "github.com/go-logr/logr" "github.com/spf13/pflag" - "go.uber.org/zap/zapcore" apiextensionsv1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" k8slabels "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" _ "k8s.io/client-go/plugin/pkg/client/auth" + "k8s.io/klog/v2" + "k8s.io/klog/v2/textlogger" ctrl "sigs.k8s.io/controller-runtime" crcache "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer" "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/server" catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" @@ -101,11 +101,8 @@ func main() { flag.StringVar(&cachePath, "cache-path", "/var/cache", "The local directory path used for filesystem based caching") flag.BoolVar(&operatorControllerVersion, "version", false, "Prints operator-controller version information") flag.StringVar(&systemNamespace, "system-namespace", "", "Configures the namespace that gets used to deploy system resources.") - opts := zap.Options{ - Development: true, - TimeEncoder: zapcore.RFC3339NanoTimeEncoder, - } - opts.BindFlags(flag.CommandLine) + + klog.InitFlags(flag.CommandLine) pflag.CommandLine.AddGoFlagSet(flag.CommandLine) features.OperatorControllerFeatureGate.AddFlag(pflag.CommandLine) @@ -116,7 +113,8 @@ func main() { os.Exit(0) } - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts), zap.StacktraceLevel(zapcore.DPanicLevel))) + ctrl.SetLogger(textlogger.NewLogger(textlogger.NewConfig())) + setupLog.Info("starting up the controller", "version info", version.String()) if systemNamespace == "" { diff --git a/config/base/manager/manager.yaml b/config/base/manager/manager.yaml index 67dedaf383..e261c5c3e9 100644 --- a/config/base/manager/manager.yaml +++ b/config/base/manager/manager.yaml @@ -90,12 +90,12 @@ spec: capabilities: drop: - "ALL" - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.15.0 args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=0" + - --secure-listen-address=0.0.0.0:8443 + - --http2-disable + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true ports: - containerPort: 8443 protocol: TCP diff --git a/go.mod b/go.mod index 8079963ade..0d7699cdc8 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,6 @@ require ( github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 - go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.16.1 @@ -33,6 +32,7 @@ require ( k8s.io/cli-runtime v0.31.1 k8s.io/client-go v0.31.1 k8s.io/component-base v0.31.1 + k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 sigs.k8s.io/controller-runtime v0.19.0 sigs.k8s.io/yaml v1.4.0 @@ -98,7 +98,6 @@ require ( github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -224,7 +223,6 @@ require ( go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.starlark.net v0.0.0-20230612165344-9532f5667272 // indirect - go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.27.0 // indirect golang.org/x/net v0.29.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect @@ -245,7 +243,6 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiserver v0.31.1 // indirect - k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/kubectl v0.31.0 // indirect oras.land/oras-go v1.2.5 // indirect diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index 399620cd48..faa3a7d5d0 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -101,8 +101,8 @@ func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Req l := log.FromContext(ctx).WithName("operator-controller") ctx = log.IntoContext(ctx, l) - l.V(1).Info("reconcile starting") - defer l.V(1).Info("reconcile ending") + l.Info("reconcile starting") + defer l.Info("reconcile ending") existingExt := &ocv1alpha1.ClusterExtension{} if err := r.Client.Get(ctx, req.NamespacedName, existingExt); err != nil { @@ -190,7 +190,7 @@ func checkForUnexpectedFieldChange(a, b ocv1alpha1.ClusterExtension) bool { func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (ctrl.Result, error) { l := log.FromContext(ctx) - l.V(1).Info("handling finalizers") + l.Info("handling finalizers") finalizeResult, err := r.Finalizers.Finalize(ctx, ext) if err != nil { // TODO: For now, this error handling follows the pattern of other error handling. @@ -210,7 +210,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp return ctrl.Result{}, nil } - l.V(1).Info("getting installed bundle") + l.Info("getting installed bundle") installedBundle, err := r.InstalledBundleGetter.GetInstalledBundle(ctx, ext) if err != nil { setInstallStatus(ext, nil) @@ -221,7 +221,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp } // run resolution - l.V(1).Info("resolving bundle") + l.Info("resolving bundle") resolvedBundle, resolvedBundleVersion, resolvedDeprecation, err := r.Resolver.Resolve(ctx, ext, installedBundle) if err != nil { // Note: We don't distinguish between resolution-specific errors and generic errors @@ -261,7 +261,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp Ref: resolvedBundle.Image, }, } - l.V(1).Info("unpacking resolved bundle") + l.Info("unpacking resolved bundle") unpackResult, err := r.Unpacker.Unpack(ctx, bundleSource) if err != nil { // Wrap the error passed to this with the resolution information until we have successfully @@ -286,7 +286,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp labels.BundleVersionKey: resolvedBundleVersion.String(), } - l.V(1).Info("applying bundle contents") + l.Info("applying bundle contents") // NOTE: We need to be cautious of eating errors here. // We should always return any error that occurs during an // attempt to apply content to the cluster. Only when there is @@ -309,7 +309,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp setInstallStatus(ext, installStatus) setInstalledStatusConditionSuccess(ext, fmt.Sprintf("Installed bundle %s successfully", resolvedBundle.Image)) - l.V(1).Info("watching managed objects") + l.Info("watching managed objects") cache, err := r.Manager.Get(ctx, ext) if err != nil { // No need to wrap error with resolution information here (or beyond) since the diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go index 06f9a31b01..7159db733b 100644 --- a/test/upgrade-e2e/post_upgrade_test.go +++ b/test/upgrade-e2e/post_upgrade_test.go @@ -57,7 +57,7 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { defer cancel() substrings := []string{ "reconcile ending", - fmt.Sprintf(`"ClusterExtension": {"name":"%s"}`, testClusterExtensionName), + fmt.Sprintf(`ClusterExtension=%q`, testClusterExtensionName), } found, err := watchPodLogsForSubstring(logCtx, &managerPods.Items[0], "manager", substrings...) require.NoError(t, err) From f169414268e3cd8d3e85b2c307142573697406af Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Fri, 27 Sep 2024 15:28:08 -0400 Subject: [PATCH 041/694] follow-ups for containers/image from catalogd and previous PR (#1270) Signed-off-by: Joe Lanford --- go.mod | 1 - go.sum | 2 - .../clusterextension_controller.go | 9 +- .../clusterextension_controller_test.go | 160 +++++++++++------- internal/rukpak/source/containers_image.go | 155 +++++++++++++---- .../rukpak/source/containers_image_test.go | 16 +- 6 files changed, 225 insertions(+), 118 deletions(-) diff --git a/go.mod b/go.mod index 0d7699cdc8..3b35708682 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/go-logr/logr v1.4.2 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.2 - github.com/olareg/olareg v0.1.1 github.com/onsi/ginkgo/v2 v2.20.2 github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 diff --git a/go.sum b/go.sum index 5c9316e6fa..124f295ce2 100644 --- a/go.sum +++ b/go.sum @@ -513,8 +513,6 @@ github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olareg/olareg v0.1.1 h1:Ui7q93zjcoF+U9U71sgqgZWByDoZOpqHitUXEu2xV+g= -github.com/olareg/olareg v0.1.1/go.mod h1:w8NP4SWrHHtxsFaUiv1lnCnYPm4sN1seCd2h7FK/dc0= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index faa3a7d5d0..4272e05e5c 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -110,8 +110,7 @@ func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Req } reconciledExt := existingExt.DeepCopy() - res, err := r.reconcile(ctx, reconciledExt) - updateError := err + res, reconcileErr := r.reconcile(ctx, reconciledExt) // Do checks before any Update()s, as Update() may modify the resource structure! updateStatus := !equality.Semantic.DeepEqual(existingExt.Status, reconciledExt.Status) @@ -130,18 +129,18 @@ func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Req finalizers := reconciledExt.Finalizers if updateStatus { if err := r.Client.Status().Update(ctx, reconciledExt); err != nil { - updateError = errors.Join(updateError, fmt.Errorf("error updating status: %v", err)) + reconcileErr = errors.Join(reconcileErr, fmt.Errorf("error updating status: %v", err)) } } reconciledExt.Finalizers = finalizers if updateFinalizers { if err := r.Client.Update(ctx, reconciledExt); err != nil { - updateError = errors.Join(updateError, fmt.Errorf("error updating finalizers: %v", err)) + reconcileErr = errors.Join(reconcileErr, fmt.Errorf("error updating finalizers: %v", err)) } } - return res, updateError + return res, reconcileErr } // ensureAllConditionsWithReason checks that all defined condition types exist in the given ClusterExtension, diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index d38404b9c2..b80ca59b0e 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -18,6 +18,7 @@ import ( "k8s.io/apimachinery/pkg/util/rand" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/operator-framework/operator-registry/alpha/declcfg" @@ -94,74 +95,103 @@ func TestClusterExtensionResolutionFails(t *testing.T) { } func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { - cl, reconciler := newClientAndReconciler(t) - reconciler.Unpacker = &MockUnpacker{ - err: errors.New("unpack failure"), + type testCase struct { + name string + unpackErr error + expectTerminal bool } - - ctx := context.Background() - extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} - - t.Log("When the cluster extension specifies a channel with version that exist") - t.Log("By initializing cluster state") - pkgName := "prometheus" - pkgVer := "1.0.0" - pkgChan := "beta" - namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) - serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - - clusterExtension := &ocv1alpha1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ - SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ - PackageName: pkgName, - Version: pkgVer, - Channels: []string{pkgChan}, - }, - }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, - }, + for _, tc := range []testCase{ + { + name: "non-terminal unpack failure", + unpackErr: errors.New("unpack failure"), }, + { + name: "terminal unpack failure", + unpackErr: reconcile.TerminalError(errors.New("terminal unpack failure")), + expectTerminal: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + cl, reconciler := newClientAndReconciler(t) + reconciler.Unpacker = &MockUnpacker{ + err: tc.unpackErr, + } + + ctx := context.Background() + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} + + t.Log("When the cluster extension specifies a channel with version that exist") + t.Log("By initializing cluster state") + pkgName := "prometheus" + pkgVer := "1.0.0" + pkgChan := "beta" + namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) + + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ + Source: ocv1alpha1.SourceConfig{ + SourceType: "Catalog", + Catalog: &ocv1alpha1.CatalogSource{ + PackageName: pkgName, + Version: pkgVer, + Channels: []string{pkgChan}, + }, + }, + Install: ocv1alpha1.ClusterExtensionInstallConfig{ + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, + }, + }, + }, + } + err := cl.Create(ctx, clusterExtension) + require.NoError(t, err) + + t.Log("It sets resolution success status") + t.Log("By running reconcile") + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + v := bsemver.MustParse("1.0.0") + return &declcfg.Bundle{ + Name: "prometheus.v1.0.0", + Package: "prometheus", + Image: "quay.io/operatorhubio/prometheus@fake1.0.0", + }, &v, nil, nil + }) + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) + require.Equal(t, ctrl.Result{}, res) + require.Error(t, err) + + isTerminal := errors.Is(err, reconcile.TerminalError(nil)) + assert.Equal(t, tc.expectTerminal, isTerminal, "expected terminal error: %v, got: %v", tc.expectTerminal, isTerminal) + assert.ErrorContains(t, err, tc.unpackErr.Error()) + + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) + + t.Log("By checking the status fields") + expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} + require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle) + require.Empty(t, clusterExtension.Status.Install) + + t.Log("By checking the expected conditions") + expectStatus := metav1.ConditionTrue + expectReason := ocv1alpha1.ReasonRetrying + if tc.expectTerminal { + expectStatus = metav1.ConditionFalse + expectReason = ocv1alpha1.ReasonBlocked + } + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, progressingCond) + require.Equal(t, expectStatus, progressingCond.Status) + require.Equal(t, expectReason, progressingCond.Reason) + require.Contains(t, progressingCond.Message, fmt.Sprintf("for resolved bundle %q with version %q", expectedBundleMetadata.Name, expectedBundleMetadata.Version)) + + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + }) } - err := cl.Create(ctx, clusterExtension) - require.NoError(t, err) - - t.Log("It sets resolution success status") - t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { - v := bsemver.MustParse("1.0.0") - return &declcfg.Bundle{ - Name: "prometheus.v1.0.0", - Package: "prometheus", - Image: "quay.io/operatorhubio/prometheus@fake1.0.0", - }, &v, nil, nil - }) - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) - require.Equal(t, ctrl.Result{}, res) - require.Error(t, err) - - t.Log("By fetching updated cluster extension after reconcile") - require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) - - t.Log("By checking the status fields") - expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} - require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle) - require.Empty(t, clusterExtension.Status.Install) - - t.Log("By checking the expected conditions") - progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(t, progressingCond) - require.Equal(t, metav1.ConditionTrue, progressingCond.Status) - require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) - require.Contains(t, progressingCond.Message, fmt.Sprintf("for resolved bundle %q with version %q", expectedBundleMetadata.Name, expectedBundleMetadata.Version)) - - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } func TestClusterExtensionUnpackUnexpectedState(t *testing.T) { diff --git a/internal/rukpak/source/containers_image.go b/internal/rukpak/source/containers_image.go index 2394e5f307..2248e64774 100644 --- a/internal/rukpak/source/containers_image.go +++ b/internal/rukpak/source/containers_image.go @@ -19,6 +19,7 @@ import ( "github.com/containers/image/v5/pkg/compression" "github.com/containers/image/v5/signature" "github.com/containers/image/v5/types" + "github.com/go-logr/logr" "github.com/opencontainers/go-digest" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -45,14 +46,9 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour // Resolve a canonical reference for the image. // ////////////////////////////////////////////////////// - imgRef, err := reference.ParseNamed(bundle.Image.Ref) + imgRef, canonicalRef, _, err := resolveReferences(ctx, bundle.Image.Ref, i.SourceContext) if err != nil { - return nil, reconcile.TerminalError(fmt.Errorf("error parsing image reference %q: %w", bundle.Image.Ref, err)) - } - - canonicalRef, err := resolveCanonicalRef(ctx, imgRef, i.SourceContext) - if err != nil { - return nil, fmt.Errorf("error resolving canonical reference: %w", err) + return nil, err } ////////////////////////////////////////////////////// @@ -64,7 +60,7 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour unpackPath := i.unpackPath(bundle.Name, canonicalRef.Digest()) if unpackStat, err := os.Stat(unpackPath); err == nil { if !unpackStat.IsDir() { - return nil, fmt.Errorf("unexpected file at unpack path %q: expected a directory", unpackPath) + panic(fmt.Sprintf("unexpected file at unpack path %q: expected a directory", unpackPath)) } l.Info("image already unpacked", "ref", imgRef.String(), "digest", canonicalRef.Digest().String()) return successResult(bundle.Name, unpackPath, canonicalRef), nil @@ -89,7 +85,11 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour if err != nil { return nil, fmt.Errorf("error creating temporary directory: %w", err) } - defer os.RemoveAll(layoutDir) + defer func() { + if err := os.RemoveAll(layoutDir); err != nil { + l.Error(err, "error removing temporary OCI layout directory") + } + }() layoutRef, err := layout.NewReference(layoutDir, canonicalRef.String()) if err != nil { @@ -102,17 +102,9 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour // a policy context for the image pull. // ////////////////////////////////////////////////////// - policy, err := signature.DefaultPolicy(i.SourceContext) - if os.IsNotExist(err) { - l.Info("no default policy found, using insecure policy") - policy, err = signature.NewPolicyFromBytes([]byte(`{"default":[{"type":"insecureAcceptAnything"}]}`)) - } - if err != nil { - return nil, fmt.Errorf("error getting policy: %w", err) - } - policyContext, err := signature.NewPolicyContext(policy) + policyContext, err := loadPolicyContext(i.SourceContext, l) if err != nil { - return nil, fmt.Errorf("error getting policy context: %w", err) + return nil, fmt.Errorf("error loading policy context: %w", err) } defer func() { if err := policyContext.Destroy(); err != nil { @@ -138,6 +130,9 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour // ////////////////////////////////////////////////////// if err := i.unpackImage(ctx, unpackPath, layoutRef); err != nil { + if cleanupErr := deleteRecursive(unpackPath); cleanupErr != nil { + err = errors.Join(err, cleanupErr) + } return nil, fmt.Errorf("error unpacking image: %w", err) } @@ -163,7 +158,7 @@ func successResult(bundleName, unpackPath string, canonicalRef reference.Canonic } func (i *ContainersImageRegistry) Cleanup(_ context.Context, bundle *BundleSource) error { - return os.RemoveAll(i.bundlePath(bundle.Name)) + return deleteRecursive(i.bundlePath(bundle.Name)) } func (i *ContainersImageRegistry) bundlePath(bundleName string) string { @@ -174,31 +169,60 @@ func (i *ContainersImageRegistry) unpackPath(bundleName string, digest digest.Di return filepath.Join(i.bundlePath(bundleName), digest.String()) } -func resolveCanonicalRef(ctx context.Context, imgRef reference.Named, imageCtx *types.SystemContext) (reference.Canonical, error) { +func resolveReferences(ctx context.Context, ref string, sourceContext *types.SystemContext) (reference.Named, reference.Canonical, bool, error) { + imgRef, err := reference.ParseNamed(ref) + if err != nil { + return nil, nil, false, reconcile.TerminalError(fmt.Errorf("error parsing image reference %q: %w", ref, err)) + } + + canonicalRef, isCanonical, err := resolveCanonicalRef(ctx, imgRef, sourceContext) + if err != nil { + return nil, nil, false, fmt.Errorf("error resolving canonical reference: %w", err) + } + return imgRef, canonicalRef, isCanonical, nil +} + +func resolveCanonicalRef(ctx context.Context, imgRef reference.Named, imageCtx *types.SystemContext) (reference.Canonical, bool, error) { if canonicalRef, ok := imgRef.(reference.Canonical); ok { - return canonicalRef, nil + return canonicalRef, true, nil } srcRef, err := docker.NewReference(imgRef) if err != nil { - return nil, reconcile.TerminalError(fmt.Errorf("error creating reference: %w", err)) + return nil, false, reconcile.TerminalError(fmt.Errorf("error creating reference: %w", err)) } imgSrc, err := srcRef.NewImageSource(ctx, imageCtx) if err != nil { - return nil, fmt.Errorf("error creating image source: %w", err) + return nil, false, fmt.Errorf("error creating image source: %w", err) } defer imgSrc.Close() imgManifestData, _, err := imgSrc.GetManifest(ctx, nil) if err != nil { - return nil, fmt.Errorf("error getting manifest: %w", err) + return nil, false, fmt.Errorf("error getting manifest: %w", err) } imgDigest, err := manifest.Digest(imgManifestData) if err != nil { - return nil, fmt.Errorf("error getting digest of manifest: %w", err) + return nil, false, fmt.Errorf("error getting digest of manifest: %w", err) + } + canonicalRef, err := reference.WithDigest(reference.TrimNamed(imgRef), imgDigest) + if err != nil { + return nil, false, fmt.Errorf("error creating canonical reference: %w", err) + } + return canonicalRef, false, nil +} + +func loadPolicyContext(sourceContext *types.SystemContext, l logr.Logger) (*signature.PolicyContext, error) { + policy, err := signature.DefaultPolicy(sourceContext) + if os.IsNotExist(err) { + l.Info("no default policy found, using insecure policy") + policy, err = signature.NewPolicyFromBytes([]byte(`{"default":[{"type":"insecureAcceptAnything"}]}`)) + } + if err != nil { + return nil, fmt.Errorf("error loading default policy: %w", err) } - return reference.WithDigest(reference.TrimNamed(imgRef), imgDigest) + return signature.NewPolicyContext(policy) } func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath string, imageReference types.ImageReference) error { @@ -217,7 +241,7 @@ func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath st return fmt.Errorf("error creating image source: %w", err) } - if err := os.MkdirAll(unpackPath, 0755); err != nil { + if err := os.MkdirAll(unpackPath, 0700); err != nil { return fmt.Errorf("error creating unpack directory: %w", err) } l := log.FromContext(ctx) @@ -236,9 +260,12 @@ func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath st l.Info("applied layer", "layer", i) return nil }(); err != nil { - return errors.Join(err, os.RemoveAll(unpackPath)) + return errors.Join(err, deleteRecursive(unpackPath)) } } + if err := setReadOnlyRecursive(unpackPath); err != nil { + return fmt.Errorf("error making unpack directory read-only: %w", err) + } return nil } @@ -249,13 +276,17 @@ func applyLayer(ctx context.Context, unpackPath string, layer io.ReadCloser) err } defer decompressed.Close() - _, err = archive.Apply(ctx, unpackPath, decompressed, archive.WithFilter(func(h *tar.Header) (bool, error) { + _, err = archive.Apply(ctx, unpackPath, decompressed, archive.WithFilter(applyLayerFilter())) + return err +} + +func applyLayerFilter() archive.Filter { + return func(h *tar.Header) (bool, error) { h.Uid = os.Getuid() h.Gid = os.Getgid() - h.Mode |= 0770 + h.Mode |= 0700 return true, nil - })) - return err + } } func (i *ContainersImageRegistry) deleteOtherImages(bundleName string, digestToKeep digest.Digest) error { @@ -269,9 +300,65 @@ func (i *ContainersImageRegistry) deleteOtherImages(bundleName string, digestToK continue } imgDirPath := filepath.Join(bundlePath, imgDir.Name()) - if err := os.RemoveAll(imgDirPath); err != nil { + if err := deleteRecursive(imgDirPath); err != nil { return fmt.Errorf("error removing image directory: %w", err) } } return nil } + +func setReadOnlyRecursive(root string) error { + if err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + + fi, err := d.Info() + if err != nil { + return err + } + + if err := func() error { + switch typ := fi.Mode().Type(); typ { + case os.ModeSymlink: + // do not follow symlinks + // 1. if they resolve to other locations in the root, we'll find them anyway + // 2. if they resolve to other locations outside the root, we don't want to change their permissions + return nil + case os.ModeDir: + return os.Chmod(path, 0500) + case 0: // regular file + return os.Chmod(path, 0400) + default: + return fmt.Errorf("refusing to change ownership of file %q with type %v", path, typ.String()) + } + }(); err != nil { + return err + } + return nil + }); err != nil { + return fmt.Errorf("error making bundle cache read-only: %w", err) + } + return nil +} + +func deleteRecursive(root string) error { + if err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + if os.IsNotExist(err) { + return nil + } + if err != nil { + return err + } + if !d.IsDir() { + return nil + } + if err := os.Chmod(path, 0700); err != nil { + return err + } + return nil + }); err != nil { + return fmt.Errorf("error making bundle cache writable for deletion: %w", err) + } + return os.RemoveAll(root) +} diff --git a/internal/rukpak/source/containers_image_test.go b/internal/rukpak/source/containers_image_test.go index 32b43d2fc3..104a857214 100644 --- a/internal/rukpak/source/containers_image_test.go +++ b/internal/rukpak/source/containers_image_test.go @@ -15,8 +15,7 @@ import ( "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/types" "github.com/google/go-containerregistry/pkg/crane" - "github.com/olareg/olareg" - "github.com/olareg/olareg/config" + "github.com/google/go-containerregistry/pkg/registry" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -62,6 +61,7 @@ func TestUnpackValidInsecure(t *testing.T) { assert.NoError(t, err) // Ensure the unpacked file matches the source content assert.Equal(t, []byte(testFileContents), unpackedFile) + assert.NoError(t, unpacker.Cleanup(context.Background(), bundleSource)) } func TestUnpackValidUsesCache(t *testing.T) { @@ -94,6 +94,7 @@ func TestUnpackValidUsesCache(t *testing.T) { // Make sure the original contents of the cache are still present. If the cached contents // were not used, we would expect the original contents to be removed. assert.DirExists(t, testCacheFilePath) + assert.NoError(t, unpacker.Cleanup(context.Background(), bundleSource)) } func TestUnpackCacheCheckError(t *testing.T) { @@ -271,8 +272,7 @@ func TestUnpackUnexpectedFile(t *testing.T) { require.NoError(t, os.WriteFile(unpackPath, []byte{}, 0600)) // Attempt to pull and unpack the image - _, err := unpacker.Unpack(context.Background(), bundleSource) - assert.ErrorContains(t, err, "expected a directory") + assert.Panics(t, func() { _, _ = unpacker.Unpack(context.Background(), bundleSource) }) } func TestUnpackCopySucceedsMountFails(t *testing.T) { @@ -327,12 +327,7 @@ func TestCleanup(t *testing.T) { } func setupRegistry(t *testing.T) (reference.NamedTagged, reference.Canonical, func()) { - regHandler := olareg.New(config.Config{ - Storage: config.ConfigStorage{ - StoreType: config.StoreMem, - }, - }) - server := httptest.NewServer(regHandler) + server := httptest.NewServer(registry.New()) serverURL, err := url.Parse(server.URL) require.NoError(t, err) @@ -353,7 +348,6 @@ func setupRegistry(t *testing.T) (reference.NamedTagged, reference.Canonical, fu cleanup := func() { server.Close() - require.NoError(t, regHandler.Close()) } return imageTagRef, imageDigestRef, cleanup } From a4a1c6c9cbb2b3dbd1b661bc4d222a231dd160e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 16:50:54 +0200 Subject: [PATCH 042/694] :seedling: Bump mkdocs-material from 9.5.37 to 9.5.39 (#1319) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.37 to 9.5.39. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.37...9.5.39) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5d71d49a13..fda76d00ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ markdown2==2.5.0 MarkupSafe==2.1.5 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.5.37 +mkdocs-material==9.5.39 mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 From 9125118ccfce95767c1b7d2f20de5109e3f36e6d Mon Sep 17 00:00:00 2001 From: Tayler Geiger Date: Mon, 30 Sep 2024 15:32:40 -0500 Subject: [PATCH 043/694] Update Installed status condition handling (#1314) Signed-off-by: Tayler Geiger --- cmd/manager/main.go | 11 +- .../clusterextension_controller.go | 13 +- .../clusterextension_controller_test.go | 202 ++++++++++++++++++ internal/finalizers/finalizers.go | 14 ++ 4 files changed, 224 insertions(+), 16 deletions(-) create mode 100644 internal/finalizers/finalizers.go diff --git a/cmd/manager/main.go b/cmd/manager/main.go index d5da75c322..72465b4dd8 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -54,6 +54,7 @@ import ( "github.com/operator-framework/operator-controller/internal/contentmanager" "github.com/operator-framework/operator-controller/internal/controllers" "github.com/operator-framework/operator-controller/internal/features" + "github.com/operator-framework/operator-controller/internal/finalizers" "github.com/operator-framework/operator-controller/internal/httputil" "github.com/operator-framework/operator-controller/internal/labels" "github.com/operator-framework/operator-controller/internal/resolve" @@ -207,7 +208,7 @@ func main() { } clusterExtensionFinalizers := crfinalizer.NewFinalizers() - if err := clusterExtensionFinalizers.Register(controllers.ClusterExtensionCleanupUnpackCacheFinalizer, finalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { + if err := clusterExtensionFinalizers.Register(controllers.ClusterExtensionCleanupUnpackCacheFinalizer, finalizers.FinalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { return crfinalizer.Result{}, unpacker.Cleanup(ctx, &source.BundleSource{Name: obj.GetName()}) })); err != nil { setupLog.Error(err, "unable to register finalizer", "finalizerKey", controllers.ClusterExtensionCleanupUnpackCacheFinalizer) @@ -258,7 +259,7 @@ func main() { } cm := contentmanager.NewManager(clientRestConfigMapper, mgr.GetConfig(), mgr.GetRESTMapper()) - err = clusterExtensionFinalizers.Register(controllers.ClusterExtensionCleanupContentManagerCacheFinalizer, finalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { + err = clusterExtensionFinalizers.Register(controllers.ClusterExtensionCleanupContentManagerCacheFinalizer, finalizers.FinalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { ext := obj.(*ocv1alpha1.ClusterExtension) err := cm.Delete(ext) return crfinalizer.Result{}, err @@ -308,12 +309,6 @@ func main() { } } -type finalizerFunc func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) - -func (f finalizerFunc) Finalize(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { - return f(ctx, obj) -} - func authFilePathIfPresent(logger logr.Logger) string { _, err := os.Stat(authFilePath) if os.IsNotExist(err) { diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index 4272e05e5c..4f30f3aa04 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -192,15 +192,9 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp l.Info("handling finalizers") finalizeResult, err := r.Finalizers.Finalize(ctx, ext) if err != nil { - // TODO: For now, this error handling follows the pattern of other error handling. - // Namely: zero just about everything out, throw our hands up, and return an error. - // This is not ideal, and we should consider a more nuanced approach that resolves - // as much status as possible before returning, or at least keeps previous state if - // it is properly labeled with its observed generation. - setInstallStatus(ext, nil) setResolutionStatus(ext, nil) setStatusProgressing(ext, err) - ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, err.Error()) + return ctrl.Result{}, err } if finalizeResult.Updated || finalizeResult.StatusUpdated { @@ -297,8 +291,11 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp // The only way to eventually recover from permission errors is to keep retrying). managedObjs, _, err := r.Applier.Apply(ctx, unpackResult.Bundle, ext, objLbls, storeLbls) if err != nil { - setInstalledStatusConditionFailed(ext, err.Error()) setStatusProgressing(ext, wrapErrorWithResolutionInfo(resolvedBundleMetadata, err)) + // If bundle is not already installed, set Installed status condition to False + if installedBundle == nil { + setInstalledStatusConditionFailed(ext, err.Error()) + } return ctrl.Result{}, err } diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index b80ca59b0e..bf0aeaa19e 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -18,6 +18,7 @@ import ( "k8s.io/apimachinery/pkg/util/rand" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/operator-framework/operator-registry/alpha/declcfg" @@ -25,6 +26,7 @@ import ( ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/conditionsets" "github.com/operator-framework/operator-controller/internal/controllers" + "github.com/operator-framework/operator-controller/internal/finalizers" "github.com/operator-framework/operator-controller/internal/resolve" "github.com/operator-framework/operator-controller/internal/rukpak/source" ) @@ -336,6 +338,105 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } +func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { + cl, reconciler := newClientAndReconciler(t) + reconciler.Unpacker = &MockUnpacker{ + result: &source.Result{ + State: source.StateUnpacked, + Bundle: fstest.MapFS{}, + }, + } + + ctx := context.Background() + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} + + t.Log("When the cluster extension specifies a channel with version that exist") + t.Log("By initializing cluster state") + pkgName := "prometheus" + pkgVer := "1.0.0" + pkgChan := "beta" + namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) + + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ + Source: ocv1alpha1.SourceConfig{ + SourceType: "Catalog", + Catalog: &ocv1alpha1.CatalogSource{ + PackageName: pkgName, + Version: pkgVer, + Channels: []string{pkgChan}, + }, + }, + Install: ocv1alpha1.ClusterExtensionInstallConfig{ + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, + }, + }, + }, + } + err := cl.Create(ctx, clusterExtension) + require.NoError(t, err) + + t.Log("It sets resolution success status") + t.Log("By running reconcile") + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + v := bsemver.MustParse("1.0.0") + return &declcfg.Bundle{ + Name: "prometheus.v1.0.0", + Package: "prometheus", + Image: "quay.io/operatorhubio/prometheus@fake1.0.0", + }, &v, nil, nil + }) + + reconciler.Manager = &MockManagedContentCacheManager{ + cache: &MockManagedContentCache{}, + } + reconciler.InstalledBundleGetter = &MockInstalledBundleGetter{ + bundle: &ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + } + reconciler.Applier = &MockApplier{ + objs: []client.Object{}, + } + + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) + require.Equal(t, ctrl.Result{}, res) + require.NoError(t, err) + + reconciler.Applier = &MockApplier{ + err: errors.New("apply failure"), + } + + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) + require.Equal(t, ctrl.Result{}, res) + require.Error(t, err) + + t.Log("By fetching updated cluster extension after reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) + + t.Log("By checking the status fields") + expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} + require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle) + require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Install.Bundle) + + t.Log("By checking the expected installed conditions") + installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + require.NotNil(t, installedCond) + require.Equal(t, metav1.ConditionTrue, installedCond.Status) + require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) + + t.Log("By checking the expected progressing conditions") + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, progressingCond) + require.Equal(t, metav1.ConditionTrue, progressingCond.Status) + require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) + require.Contains(t, progressingCond.Message, fmt.Sprintf("for resolved bundle %q with version %q", expectedBundleMetadata.Name, expectedBundleMetadata.Version)) + + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) +} + func TestClusterExtensionManagerFailed(t *testing.T) { cl, reconciler := newClientAndReconciler(t) reconciler.Unpacker = &MockUnpacker{ @@ -591,6 +692,107 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } +func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { + cl, reconciler := newClientAndReconciler(t) + reconciler.Unpacker = &MockUnpacker{ + result: &source.Result{ + State: source.StateUnpacked, + Bundle: fstest.MapFS{}, + }, + } + + ctx := context.Background() + extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} + + t.Log("When the cluster extension specifies a channel with version that exist") + t.Log("By initializing cluster state") + pkgName := "prometheus" + pkgVer := "1.0.0" + pkgChan := "beta" + namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) + + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, + Spec: ocv1alpha1.ClusterExtensionSpec{ + Source: ocv1alpha1.SourceConfig{ + SourceType: "Catalog", + Catalog: &ocv1alpha1.CatalogSource{ + PackageName: pkgName, + Version: pkgVer, + Channels: []string{pkgChan}, + }, + }, + Install: ocv1alpha1.ClusterExtensionInstallConfig{ + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, + }, + }, + }, + } + err := cl.Create(ctx, clusterExtension) + require.NoError(t, err) + t.Log("It sets resolution success status") + t.Log("By running reconcile") + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + v := bsemver.MustParse("1.0.0") + return &declcfg.Bundle{ + Name: "prometheus.v1.0.0", + Package: "prometheus", + Image: "quay.io/operatorhubio/prometheus@fake1.0.0", + }, &v, nil, nil + }) + fakeFinalizer := "fake.testfinalizer.io" + finalizersMessage := "still have finalizers" + reconciler.Applier = &MockApplier{ + objs: []client.Object{}, + } + reconciler.Manager = &MockManagedContentCacheManager{ + cache: &MockManagedContentCache{}, + } + reconciler.InstalledBundleGetter = &MockInstalledBundleGetter{ + bundle: &ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + } + err = reconciler.Finalizers.Register(fakeFinalizer, finalizers.FinalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { + return crfinalizer.Result{}, errors.New(finalizersMessage) + })) + + require.NoError(t, err) + + // Reconcile twice to simulate installing the ClusterExtension and loading in the finalizers + res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) + require.Equal(t, ctrl.Result{}, res) + require.NoError(t, err) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) + require.Equal(t, ctrl.Result{}, res) + require.NoError(t, err) + + t.Log("By fetching updated cluster extension after first reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} + require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Install.Bundle) + require.NotNil(t, cond) + require.Equal(t, metav1.ConditionTrue, cond.Status) + + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) + require.Error(t, err, res) + + t.Log("By fetching updated cluster extension after second reconcile") + require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Install.Bundle) + require.NotNil(t, cond) + require.Equal(t, metav1.ConditionTrue, cond.Status) + require.Equal(t, fakeFinalizer, clusterExtension.Finalizers[0]) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + require.NotNil(t, cond) + require.Equal(t, metav1.ConditionTrue, cond.Status) + require.Contains(t, cond.Message, finalizersMessage) +} + func verifyInvariants(ctx context.Context, t *testing.T, c client.Client, ext *ocv1alpha1.ClusterExtension) { key := client.ObjectKeyFromObject(ext) require.NoError(t, c.Get(ctx, key, ext)) diff --git a/internal/finalizers/finalizers.go b/internal/finalizers/finalizers.go new file mode 100644 index 0000000000..b04635a9e7 --- /dev/null +++ b/internal/finalizers/finalizers.go @@ -0,0 +1,14 @@ +package finalizers + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer" +) + +type FinalizerFunc func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) + +func (f FinalizerFunc) Finalize(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { + return f(ctx, obj) +} From f14bca4aca30c37cd3007ad6393069f968b54de5 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Mon, 30 Sep 2024 23:42:54 -0400 Subject: [PATCH 044/694] dynamicsource: fix race condition with wait and valid scheme (#1323) Signed-off-by: Joe Lanford --- internal/contentmanager/source/dynamicsource.go | 2 +- internal/contentmanager/source/dynamicsource_test.go | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/contentmanager/source/dynamicsource.go b/internal/contentmanager/source/dynamicsource.go index 1f539871f0..c7c7b3bd56 100644 --- a/internal/contentmanager/source/dynamicsource.go +++ b/internal/contentmanager/source/dynamicsource.go @@ -139,7 +139,7 @@ func (dis *dynamicInformerSource) Start(ctx context.Context, q workqueue.TypedRa close(dis.syncedChan) }) - _ = wait.PollUntilContextCancel(dis.informerCtx, time.Second, true, func(_ context.Context) (bool, error) { + _ = wait.PollUntilContextCancel(dis.informerCtx, time.Millisecond*100, true, func(_ context.Context) (bool, error) { if sharedIndexInf.HasSynced() { syncOnce() return true, nil diff --git a/internal/contentmanager/source/dynamicsource_test.go b/internal/contentmanager/source/dynamicsource_test.go index 61cbab8ca5..ea1d653f3b 100644 --- a/internal/contentmanager/source/dynamicsource_test.go +++ b/internal/contentmanager/source/dynamicsource_test.go @@ -7,10 +7,10 @@ import ( "time" "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic/dynamicinformer" dynamicfake "k8s.io/client-go/dynamic/fake" + "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" @@ -92,7 +92,7 @@ func TestDynamicInformerSourceStartAlreadyStarted(t *testing.T) { } func TestDynamicInformerSourceStart(t *testing.T) { - fakeDynamicClient := dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()) + fakeDynamicClient := dynamicfake.NewSimpleDynamicClient(scheme.Scheme) infFact := dynamicinformer.NewDynamicSharedInformerFactory(fakeDynamicClient, time.Minute) dis := NewDynamicSource(DynamicSourceConfig{ DynamicInformerFactory: infFact, @@ -108,5 +108,9 @@ func TestDynamicInformerSourceStart(t *testing.T) { }) require.NoError(t, dis.Start(context.Background(), nil)) + + waitCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + require.NoError(t, dis.WaitForSync(waitCtx)) require.NoError(t, dis.Close()) } From 024c78bdcdd0dfd4888a11a68e9e4372bdbd2cf4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:59:50 +0200 Subject: [PATCH 045/694] :seedling: Bump pymdown-extensions from 10.10.2 to 10.11.1 (#1321) Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.10.2 to 10.11.1. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.10.2...10.11.1) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fda76d00ba..350064cabc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 Pygments==2.18.0 -pymdown-extensions==10.10.2 +pymdown-extensions==10.11.1 pyquery==2.0.1 python-dateutil==2.9.0.post0 PyYAML==6.0.2 From fba8473cfd98d99ea74972d7e8951de46ec16eea Mon Sep 17 00:00:00 2001 From: Lalatendu Mohanty Date: Tue, 1 Oct 2024 06:13:18 -0400 Subject: [PATCH 046/694] :warning: Remove status.resolution (#1315) * Remove status.resolution Fixes #1306 Signed-off-by: Lalatendu Mohanty * Improving the unit test code readability replacing below code with require.NotNil(ct, cond) ``` if !assert.NotNil(ct, cond) { return } ``` Signed-off-by: Lalatendu Mohanty --------- Signed-off-by: Lalatendu Mohanty --- api/v1alpha1/clusterextension_types.go | 12 -- api/v1alpha1/zz_generated.deepcopy.go | 21 --- ...peratorframework.io_clusterextensions.yaml | 28 --- .../api/operator-controller-api-reference.md | 18 -- .../clusterextension_controller.go | 8 - .../clusterextension_controller_test.go | 7 - internal/controllers/common_controller.go | 4 - test/e2e/cluster_extension_install_test.go | 174 ++---------------- test/upgrade-e2e/post_upgrade_test.go | 1 - 9 files changed, 20 insertions(+), 253 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index 25c0a32b21..b68f7f2a77 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -470,8 +470,6 @@ type BundleMetadata struct { type ClusterExtensionStatus struct { Install *ClusterExtensionInstallStatus `json:"install,omitempty"` - Resolution *ClusterExtensionResolutionStatus `json:"resolution,omitempty"` - // conditions is a representation of the current state for this ClusterExtension. // The status is represented by a set of "conditions". // @@ -512,16 +510,6 @@ type ClusterExtensionInstallStatus struct { Bundle BundleMetadata `json:"bundle"` } -type ClusterExtensionResolutionStatus struct { - // bundle is a representation of the bundle that was identified during - // resolution to meet all installation/upgrade constraints and is slated to be - // installed or upgraded to. - // - // A "bundle" is a versioned set of content that represents the resources that - // need to be applied to a cluster to install a package. - Bundle BundleMetadata `json:"bundle"` -} - //+kubebuilder:object:root=true //+kubebuilder:resource:scope=Cluster //+kubebuilder:subresource:status diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ed65d696b9..ccd143aec5 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -172,22 +172,6 @@ func (in *ClusterExtensionList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterExtensionResolutionStatus) DeepCopyInto(out *ClusterExtensionResolutionStatus) { - *out = *in - out.Bundle = in.Bundle -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionResolutionStatus. -func (in *ClusterExtensionResolutionStatus) DeepCopy() *ClusterExtensionResolutionStatus { - if in == nil { - return nil - } - out := new(ClusterExtensionResolutionStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterExtensionSpec) DeepCopyInto(out *ClusterExtensionSpec) { *out = *in @@ -213,11 +197,6 @@ func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) { *out = new(ClusterExtensionInstallStatus) **out = **in } - if in.Resolution != nil { - in, out := &in.Resolution, &out.Resolution - *out = new(ClusterExtensionResolutionStatus) - **out = **in - } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]v1.Condition, len(*in)) diff --git a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml index c25c2ccb2a..f24871f004 100644 --- a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml +++ b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -572,34 +572,6 @@ spec: required: - bundle type: object - resolution: - properties: - bundle: - description: |- - bundle is a representation of the bundle that was identified during - resolution to meet all installation/upgrade constraints and is slated to be - installed or upgraded to. - - A "bundle" is a versioned set of content that represents the resources that - need to be applied to a cluster to install a package. - properties: - name: - description: |- - name is a required field and is a reference - to the name of a bundle - type: string - version: - description: |- - version is a required field and is a reference - to the version that this bundle represents - type: string - required: - - name - - version - type: object - required: - - bundle - type: object type: object type: object served: true diff --git a/docs/refs/api/operator-controller-api-reference.md b/docs/refs/api/operator-controller-api-reference.md index de0a1f2bd1..0c5aced18a 100644 --- a/docs/refs/api/operator-controller-api-reference.md +++ b/docs/refs/api/operator-controller-api-reference.md @@ -24,7 +24,6 @@ Package v1alpha1 contains API Schema definitions for the olm v1alpha1 API group _Appears in:_ - [ClusterExtensionInstallStatus](#clusterextensioninstallstatus) -- [ClusterExtensionResolutionStatus](#clusterextensionresolutionstatus) | Field | Description | Default | Validation | | --- | --- | --- | --- | @@ -162,22 +161,6 @@ ClusterExtensionList contains a list of ClusterExtension | `items` _[ClusterExtension](#clusterextension) array_ | | | | -#### ClusterExtensionResolutionStatus - - - - - - - -_Appears in:_ -- [ClusterExtensionStatus](#clusterextensionstatus) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `bundle` _[BundleMetadata](#bundlemetadata)_ | bundle is a representation of the bundle that was identified during
resolution to meet all installation/upgrade constraints and is slated to be
installed or upgraded to.

A "bundle" is a versioned set of content that represents the resources that
need to be applied to a cluster to install a package. | | | - - #### ClusterExtensionSpec @@ -209,7 +192,6 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `install` _[ClusterExtensionInstallStatus](#clusterextensioninstallstatus)_ | | | | -| `resolution` _[ClusterExtensionResolutionStatus](#clusterextensionresolutionstatus)_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterExtension.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human readable message that further elaborates on the state of the condition

The global set of condition types are:
- "Installed", represents whether or not the a bundle has been installed for this ClusterExtension
- "Resolved", represents whether or not a bundle was found that satisfies the selection criteria outlined in the spec
- "Unpacked", represents whether or not the bundle contents have been successfully unpacked

When the ClusterExtension is sourced from a catalog, the following conditions are also possible:
- "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types
- "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated
- "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated
- "BundleDeprecated", represents whether or not the installed bundle is deprecated

The current set of reasons are:
- "Success", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when unpacking a bundle's content, resolution and installation/upgrading is successful
- "Failed", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when an error has occurred while unpacking the contents of a bundle, during resolution or installation. | | | diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index 4f30f3aa04..65cb2eaf64 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -192,9 +192,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp l.Info("handling finalizers") finalizeResult, err := r.Finalizers.Finalize(ctx, ext) if err != nil { - setResolutionStatus(ext, nil) setStatusProgressing(ext, err) - return ctrl.Result{}, err } if finalizeResult.Updated || finalizeResult.StatusUpdated { @@ -219,7 +217,6 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp if err != nil { // Note: We don't distinguish between resolution-specific errors and generic errors setInstallStatus(ext, nil) - setResolutionStatus(ext, nil) setStatusProgressing(ext, err) ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, err.Error()) return ctrl.Result{}, err @@ -242,11 +239,6 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp SetDeprecationStatus(ext, resolvedBundle.Name, resolvedDeprecation) resolvedBundleMetadata := bundleutil.MetadataFor(resolvedBundle.Name, *resolvedBundleVersion) - resStatus := &ocv1alpha1.ClusterExtensionResolutionStatus{ - Bundle: resolvedBundleMetadata, - } - setResolutionStatus(ext, resStatus) - bundleSource := &rukpaksource.BundleSource{ Name: ext.GetName(), Type: rukpaksource.SourceTypeImage, diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index bf0aeaa19e..79f3be476c 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -82,7 +82,6 @@ func TestClusterExtensionResolutionFails(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Empty(t, clusterExtension.Status.Resolution) require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected conditions") @@ -175,7 +174,6 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { t.Log("By checking the status fields") expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} - require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle) require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected conditions") @@ -319,7 +317,6 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) t.Log("By checking the status fields") expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} - require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle) require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected installed conditions") @@ -418,7 +415,6 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { t.Log("By checking the status fields") expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} - require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle) require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Install.Bundle) t.Log("By checking the expected installed conditions") @@ -503,7 +499,6 @@ func TestClusterExtensionManagerFailed(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) t.Log("By checking the expected installed conditions") @@ -590,7 +585,6 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) t.Log("By checking the expected installed conditions") @@ -674,7 +668,6 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle) require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) t.Log("By checking the expected installed conditions") diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index 1ef5a18cfa..8210f39997 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -48,10 +48,6 @@ func setInstalledStatusConditionFailed(ext *ocv1alpha1.ClusterExtension, message }) } -func setResolutionStatus(ext *ocv1alpha1.ClusterExtension, resStatus *ocv1alpha1.ClusterExtensionResolutionStatus) { - ext.Status.Resolution = resStatus -} - func setInstallStatus(ext *ocv1alpha1.ClusterExtension, installStatus *ocv1alpha1.ClusterExtensionInstallStatus) { ext.Status.Install = installStatus } diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 351f777f51..1e32166cbe 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -268,22 +268,13 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: fmt.Sprintf("%s-operator.1.2.0", tc.packageName), - Version: "1.2.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) t.Log("By eventually reporting no longer progressing") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -292,9 +283,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") @@ -332,16 +321,13 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { t.Log("By eventually reporting a failed resolution with multiple bundles") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - assert.Nil(ct, clusterExtension.Status.Resolution) }, pollDuration, pollInterval) t.Log("By eventually reporting Progressing == True and Reason Retrying") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [operatorhubio test-catalog]") @@ -377,13 +363,6 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { t.Log("By eventually reporting a successful installation") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.0.0", - Version: "1.0.0", - }}, - clusterExtension.Status.Resolution, - ) assert.Equal(ct, &ocv1alpha1.ClusterExtensionInstallStatus{Bundle: ocv1alpha1.BundleMetadata{ Name: "prometheus-operator.1.0.0", @@ -393,9 +372,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { ) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -408,16 +385,13 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { t.Log("By eventually reporting an unsatisfiable resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - assert.Empty(ct, clusterExtension.Status.Resolution) }, pollDuration, pollInterval) t.Log("By eventually reporting Progressing == True and Reason Retrying") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"prometheus\" matching version \"1.2.0\"", cond.Message) }, pollDuration, pollInterval) @@ -452,19 +426,9 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.0.0", - Version: "1.0.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) t.Log("It allows to upgrade the ClusterExtension to a non-successor version") @@ -477,19 +441,9 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.2.0", - Version: "1.2.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) } @@ -521,19 +475,9 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.0.0", - Version: "1.0.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) t.Log("It does allow to upgrade the ClusterExtension to any of the successor versions within non-zero major version") @@ -545,19 +489,9 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.0.1", - Version: "1.0.1", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) } @@ -599,19 +533,9 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.2.0", - Version: "1.2.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) // patch imageRef tag on test-catalog image with v2 image @@ -622,9 +546,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeServing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) }, pollDuration, pollInterval) @@ -633,19 +555,9 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.2.0.0", - Version: "2.0.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) } @@ -699,19 +611,9 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.2.0", - Version: "1.2.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) // update tag on test-catalog image with v2 image @@ -722,9 +624,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeServing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) }, pollDuration, pollInterval) @@ -733,19 +633,9 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.2.0.0", - Version: "2.0.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) } @@ -781,19 +671,10 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.2.0", - Version: "1.2.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) t.Log("By deleting a managed resource") @@ -853,22 +734,13 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - assert.Equal(ct, - &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{ - Name: "prometheus-operator.1.2.0", - Version: "1.2.0", - }}, - clusterExtension.Status.Resolution, - ) }, pollDuration, pollInterval) t.Log("By eventually reporting Progressing == True with Reason Retrying") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) }, pollDuration, pollInterval) @@ -877,9 +749,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonFailed, cond.Reason) assert.Contains(ct, cond.Message, "forbidden") @@ -896,9 +766,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") @@ -909,9 +777,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return - } + require.NotNil(ct, cond) assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go index 7159db733b..8bb2ae3df6 100644 --- a/test/upgrade-e2e/post_upgrade_test.go +++ b/test/upgrade-e2e/post_upgrade_test.go @@ -107,7 +107,6 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { } assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") - assert.Equal(ct, ocv1alpha1.BundleMetadata{Name: "prometheus-operator.1.0.1", Version: "1.0.1"}, clusterExtension.Status.Resolution.Bundle) assert.Equal(ct, ocv1alpha1.BundleMetadata{Name: "prometheus-operator.1.0.1", Version: "1.0.1"}, clusterExtension.Status.Install.Bundle) assert.NotEqual(ct, previousVersion, clusterExtension.Status.Install.Bundle.Version) }, time.Minute, time.Second) From de17da95339e37270bbe2d2e725f307672497cb5 Mon Sep 17 00:00:00 2001 From: Rashmi Khanna Date: Tue, 1 Oct 2024 18:14:55 +0530 Subject: [PATCH 047/694] =?UTF-8?q?=F0=9F=93=96=20Derive=20minimal=20servi?= =?UTF-8?q?ce=20account=20needed=20to=20install=20a=20bundle=20(#1238)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * changes to derice minimum service account Signed-off-by: rashmi_kh * remove titles Signed-off-by: rashmi_kh * update content to use unordered lists Signed-off-by: rashmi_kh * update huge example as a note Signed-off-by: rashmi_kh * deploy example as yaml Signed-off-by: rashmi_kh * change order Signed-off-by: rashmi_kh * render sh correctly Signed-off-by: rashmi_kh * add new line between roles Signed-off-by: rashmi_kh * update service account Signed-off-by: rashmi_kh * address review comments Signed-off-by: rashmi_kh * remove bundle info. Signed-off-by: rashmi_kh * review comments, link to docs Signed-off-by: rashmi_kh * review comments Signed-off-by: rashmi_kh * remove sa related info Signed-off-by: rashmi_kh * update permissions list Signed-off-by: rashmi_kh * permissions list update Signed-off-by: rashmi_kh * provide an example Signed-off-by: rashmi_kh * address review comments Signed-off-by: rashmi_kh * try collapsible block Signed-off-by: rashmi_kh * try collapsible block Signed-off-by: rashmi_kh * try collapsible block Signed-off-by: rashmi_kh * try collapsible block Signed-off-by: rashmi_kh * try collapsible block Signed-off-by: rashmi_kh * final collapsible block Signed-off-by: rashmi_kh * final collapsible block Signed-off-by: rashmi_kh * address review comments Signed-off-by: rashmi_kh * address review comments from per Signed-off-by: rashmi_kh * address review comments from per Signed-off-by: rashmi_kh * address review comments from per Signed-off-by: rashmi_kh * address review comments from per, divide into 2 sections Signed-off-by: rashmi_kh * address review comments from per, clean up Signed-off-by: rashmi_kh * address review comments from per, add examples Signed-off-by: rashmi_kh * refactor to new template Signed-off-by: rashmi_kh * refactor to new template Signed-off-by: rashmi_kh * refactor to new template Signed-off-by: rashmi_kh * add links to CSV Signed-off-by: rashmi_kh * add roles Signed-off-by: rashmi_kh * remove yml Signed-off-by: rashmi_kh * move to samples Signed-off-by: rashmi_kh * remove samples Signed-off-by: rashmi_kh * remove samples Signed-off-by: rashmi_kh * add services permissions Signed-off-by: rashmi_kh * add services permissions Signed-off-by: rashmi_kh * add Pers updates Signed-off-by: rashmi_kh * add link to argocd and add disclaimer Signed-off-by: rashmi_kh * add link to argocd yamls Signed-off-by: rashmi_kh * add link to argocd yamls Signed-off-by: rashmi_kh Signed-off-by: Per Goncalves da Silva --------- Signed-off-by: rashmi_kh Signed-off-by: Per Goncalves da Silva --- docs/drafts/derive-serviceaccount.md | 351 +++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 docs/drafts/derive-serviceaccount.md diff --git a/docs/drafts/derive-serviceaccount.md b/docs/drafts/derive-serviceaccount.md new file mode 100644 index 0000000000..fec1649df7 --- /dev/null +++ b/docs/drafts/derive-serviceaccount.md @@ -0,0 +1,351 @@ +# Derive minimal ServiceAccount required for ClusterExtension Installation and Management + +OLM v1 does not have permission to install extensions on a cluster by default. In order to install a [supported bundle](../refs/supported-extensions.md), +OLM must be provided a ServiceAccount configured with the appropriate permissions. For more information, see the [provided ServiceAccount](./provided-serviceaccount.md) documentation. + +This document serves as a guide for how to derive the RBAC necessary to install a bundle. + +### Required RBAC + +The required permissions for the installation and management of a cluster extension can be determined by examining the contents of its bundle image. +This bundle image contains all the manifests that make up the extension (e.g. `CustomResourceDefinition`s, `Service`s, `Secret`s, `ConfigMap`s, `Deployment`s etc.) +as well as a [`ClusterServiceVersion`](https://olm.operatorframework.io/docs/concepts/crds/clusterserviceversion/) (CSV) that describes the extension and its service account's permission requirements. + +The service account must have permissions to: + - create and manage the extension's `CustomResourceDefinition`s + - create and manage the resources packaged in the bundle + - grant the extension controller's service account the permissions it requires for its operation + - create and manage the extension controller's service account + - create and manage the `Role`s, `RoleBinding`s, `ClusterRole`s, and `ClusterRoleBinding`s associated with the extension controller's service account + - create and manage the extension controller's deployment + +Additionally, for clusters that use the [OwnerReferencesPermissionEnforcement](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement) admission plug-in, the service account must also have permissions to: + - update finalizers on the ClusterExtension to be able to set blockOwnerDeletion and ownerReferences + +It is good security practice to follow the [principle of least privilege(https://en.wikipedia.org/wiki/Principle_of_least_privilege)], and scope permissions to specific resource names, wherever possible. +Keep in mind, that it is not possible to scope `create`, `list`, and `watch` permissions to specific resource names. + +Depending on the scope, each permission will need to be added to either a `ClusterRole` or a `Role` and then bound to the service account with a `ClusterRoleBinding` or a `RoleBinding`. + +### Example + +The following example illustrates the process of deriving the minimal RBAC required to install the [ArgoCD Operator](https://operatorhub.io/operator/argocd-operator) [v0.6.0](https://operatorhub.io/operator/argocd-operator/alpha/argocd-operator.v0.6.0) provided by [OperatorHub.io](https://operatorhub.io/). +The final permission set can be found in the [ClusterExtension sample manifest](../../config/samples/olm_v1alpha1_clusterextension.yaml) in the [samples](../../config/samples/olm_v1alpha1_clusterextension.yaml) directory. + +The bundle includes the following manifests, which can be found [here](https://github.com/argoproj-labs/argocd-operator/tree/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0): + +* `ClusterServiceVersion`: + - [argocd-operator.v0.6.0.clusterserviceversion.yaml](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator.v0.6.0.clusterserviceversion.yaml) +* `CustomResourceDefinition`s: + - [argoproj.io_applicationsets.yaml](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argoproj.io_applicationsets.yaml) + - [argoproj.io_applications.yaml](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argoproj.io_applications.yaml) + - [argoproj.io_appprojects.yaml](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argoproj.io_appprojects.yaml) + - [argoproj.io_argocdexports.yaml](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argoproj.io_argocdexports.yaml) + - [argoproj.io_argocds.yaml](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argoproj.io_argocds.yaml) +* Additional resources: + - [argocd-operator-controller-manager-metrics-service_v1_service.yaml](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator-controller-manager-metrics-service_v1_service.yaml) + - [argocd-operator-manager-config_v1_configmap.yaml](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator-manager-config_v1_configmap.yaml) + - [argocd-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml) + +The `ClusterServiceVersion` defines a single `Deployment` in `spec.install.deployments` named `argocd-operator-controller-manager` with a `ServiceAccount` of the same name. +It declares the following cluster-scoped permissions in `spec.install.clusterPermissions`, and its namespace-scoped permissions in `spec.install.permissions`. + +#### Derive permissions for the installer service account `ClusterRole` + +##### Step 1. RBAC creation and management permissions + +The installer service account must create and manage the `ClusterRole`s and `ClusterRoleBinding`s for the extension controller(s). +Therefore, it must have the following permissions: + +```yaml +- apiGroups: [rbac.authorization.k8s.io] + resources: [clusterroles] + verbs: [create, list, watch] +- apiGroups: [rbac.authorization.k8s.io] + resources: [clusterroles] + verbs: [get, update, patch, delete] + resourceNames: [, ...] +- apiGroups: [rbac.authorization.k8s.io] + resources: [clusterrolebindings] + verbs: [create, list, watch] +- apiGroups: [rbac.authorization.k8s.io] + resources: [clusterrolebindings] + verbs: [get, update, patch, delete] + resourceNames: [, ...] +``` + +Note: The `resourceNames` field should be populated with the names of the `ClusterRole`s and `ClusterRoleBinding`s created by OLM v1. +These names are generated with the following format: `.`. Since it is not a trivial task +to generate these names ahead of time, it is recommended to use a wildcard `*` in the `resourceNames` field for the installation. +Then, update the `resourceNames` fields by inspecting the cluster for the generated resource names. For instance, for `ClusterRole`s: + +```terminal +kubectl get clusterroles | grep argocd +``` + +Example output: + +```terminal +argocd-installer-clusterrole 2024-09-30T08:02:09Z +argocd-installer-rbac-clusterrole 2024-09-30T08:02:09Z +argocd-operator-metrics-reader 2024-09-30T08:02:12Z +# The following are the generated ClusterRoles +argocd-operator.v0-1dhiybrldl1gyksid1dk2dqjsc72psdybc7iyvse5gpx 2024-09-30T08:02:12Z +argocd-operator.v0-22gmilmgp91wu25is5i2ec598hni8owq3l71bbkl7iz3 2024-09-30T08:02:12Z +``` + +The same can be done for `ClusterRoleBindings`. + +##### Step 2. `CustomResourceDefinition` permissions + +The installer service account must be able to create and manage the `CustomResourceDefinition`s for the extension, as well +as grant the extension controller's service account the permissions it needs to manage its CRDs. + +```yaml +- apiGroups: [apiextensions.k8s.io] + resources: [customresourcedefinitions] + verbs: [create, list, watch] +- apiGroups: [apiextensions.k8s.io] + resources: [customresourcedefinitions] + verbs: [get, update, patch, delete] + # Scoped to the CRDs in the bundle + resourceNames: [applications.argoproj.io, appprojects.argoproj.io, argocds.argoproj.io, argocdexports.argoproj.io, applicationsets.argoproj.io] +``` + +##### Step 3. `OwnerReferencesPermissionEnforcement` permissions + +For clusters that use `OwnerReferencesPermissionEnforcement`, the installer service account must be able to update finalizers on the ClusterExtension to be able to set blockOwnerDeletion and ownerReferences for clusters that use `OwnerReferencesPermissionEnforcement`. +This is only a requirement for clusters that use the [OwnerReferencesPermissionEnforcement](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement) admission plug-in. + +```yaml +- apiGroups: [olm.operatorframework.io] + resources: [clusterextensions/finalizers] + verbs: [update] + # Scoped to the name of the ClusterExtension + resourceNames: [argocd-operator.v0.6.0] +``` + +##### Step 4. Bundled cluster-scoped resource permissions + +Permissions must be added for the creation and management of any cluster-scoped resources included in the bundle. +In this example, the ArgoCD bundle contains a `ClusterRole` called `argocd-operator-metrics-reader`. Given that +`ClusterRole` permissions have already been created in [Step 1](#step-1-rbac-creation-and-management-permissions), it +is sufficient to add the `argocd-operator-metrics-reader`resource name to the `resourceName` list of the pre-existing rule: + +```yaml +- apiGroups: [rbac.authorization.k8s.io] + resources: [clusterroles] + verbs: [get, update, patch, delete] + resourceNames: [, ..., argocd-operator-metrics-reader] +``` + +##### Step 5. Operator permissions declared in the ClusterServiceVersion + +Include all permissions defined in the `.spec.install.permissions` ([reference](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator.v0.6.0.clusterserviceversion.yaml#L1091)) and `.spec.install.clusterPermissions` ([reference](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator.v0.6.0.clusterserviceversion.yaml#L872)) stanzas in the bundle's `ClusterServiceVersion`. +These permissions are required by the extension controller, and therefore the installer service account must be able to grant them. + +Note: there may be overlap between the rules defined in each stanza. Overlapping rules needn't be added twice. + +```yaml +# from .spec.install.clusterPermissions +- apiGroups: [""] + resources: ["configmaps", "endpoints", "events", "namespaces", "persistentvolumeclaims", "pods", "secrets", "serviceaccounts", "services", "services/finalizers"] + verbs: ["*"] +- apiGroups: [""] + resources: ["pods", "pods/log"] + verbs: ["get"] +- apiGroups: ["apps"] + resources: ["daemonsets", "deployments", "replicasets", "statefulsets"] + verbs: ["*"] +- apiGroups: ["apps"] + resourceNames: ["argocd-operator"] + resources: ["deployments/finalizers"] + verbs: ["update"] +- apiGroups: ["apps.openshift.io"] + resources: ["deploymentconfigs"] + verbs: ["*"] +- apiGroups: ["argoproj.io"] + resources: ["applications", "appprojects"] + verbs: ["*"] +- apiGroups: ["argoproj.io"] + resources: ["argocdexports", "argocdexports/finalizers", "argocdexports/status"] + verbs: ["*"] +- apiGroups: ["argoproj.io"] + resources: ["argocds", "argocds/finalizers", "argocds/status"] + verbs: ["*"] +- apiGroups: ["autoscaling"] + resources: ["horizontalpodautoscalers"] + verbs: ["*"] +- apiGroups: ["batch"] + resources: ["cronjobs", "jobs"] + verbs: ["*"] +- apiGroups: ["config.openshift.io"] + resources: ["clusterversions"] + verbs: ["get", "list", "watch"] +- apiGroups: ["monitoring.coreos.com"] + resources: ["prometheuses", "servicemonitors"] + verbs: ["*"] +- apiGroups: ["networking.k8s.io"] + resources: ["ingresses"] + verbs: ["*"] +- apiGroups: ["oauth.openshift.io"] + resources: ["oauthclients"] + verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] +- apiGroups: ["rbac.authorization.k8s.io"] + resources: ["*"] + verbs: ["*"] +- apiGroups: ["rbac.authorization.k8s.io"] + resources: ["clusterrolebindings", "clusterroles"] + verbs: ["*"] +- apiGroups: ["route.openshift.io"] + resources: ["routes", "routes/custom-host"] + verbs: ["*"] +- apiGroups: ["template.openshift.io"] + resources: ["templateconfigs", "templateinstances", "templates"] + verbs: ["*"] +- apiGroups: ["authentication.k8s.io"] + resources: ["tokenreviews"] + verbs: ["create"] +- apiGroups: ["authorization.k8s.io"] + resources: ["subjectaccessreviews"] + verbs: ["create"] + +# copied from .spec.install.permissions +- apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] +# overlapping permissions: +# - apiGroups: [""] +# resources: ["configmaps"] +# verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] +# - apiGroups: [""] +# resources: ["events"] +# verbs: ["create", "patch"] +``` + +#### Derive permissions for the installer service account `Role` + +The following steps detail how to define the namespace-scoped permissions needed by the installer service account's `Role`. +The installer service account must create and manage the `RoleBinding`s for the extension controller(s). + +##### Step 1. `Deployment` permissions + +The installer service account must be able to create and manage the `Deployment`s for the extension controller(s). +The `Deployment` name(s) can be found in the `ClusterServiceVersion` resource packed in the bundle under `.spec.install.deployments` ([reference](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator.v0.6.0.clusterserviceversion.yaml#L1029)). +This example's `ClusterServiceVersion` can be found [here](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator.v0.6.0.clusterserviceversion.yaml). + +```yaml +- apiGroups: [apps] + resources: [deployments] + verbs: [create] +- apiGroups: [apps] + resources: [deployments] + verbs: [get, list, watch, update, patch, delete] + # scoped to the extension controller deployment name + resourceNames: [argocd-operator-controller-manager] +``` + +##### Step 2: `ServiceAccount` permissions + +The installer service account must be able to create and manage the `ServiceAccount`(s) for the extension controller(s). +The `ServiceAccount` name(s) can be found in deployment template in the `ClusterServiceVersion` resource packed in the bundle under `.spec.install.deployments`. +This example's `ClusterServiceVersion` can be found [here](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator.v0.6.0.clusterserviceversion.yaml). + +```yaml +- apiGroups: [""] + resources: [serviceaccounts] + verbs: [create, list, watch] +- apiGroups: [""] + resources: [serviceaccounts] + verbs: [get, update, patch, delete] + # scoped to the extension controller's deployment service account + resourceNames: [argocd-operator-controller-manager] +``` + +##### Step 3. Bundled namespace-scoped resource permissions + +The installer service account must also create and manage other namespace-scoped resources included in the bundle. +In this example, the bundle also includes two additional namespace-scoped resources: + * the [argocd-operator-controller-manager-metrics-service](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator-controller-manager-metrics-service_v1_service.yaml) `Service`, and + * the [argocd-operator-manager-config](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator-manager-config_v1_configmap.yaml) `ConfigMap` + +Therefore, the following permissions must be given to the installer service account: + +```yaml +- apiGroups: [""] + resources: [services] + verbs: [create] +- apiGroups: [""] + resources: [services] + verbs: [get, list, watch, update, patch, delete] + # scoped to the service name + resourceNames: [argocd-operator-controller-manager-metrics-service] +- apiGroups: [""] + resources: [configmaps] + verbs: [create] +- apiGroups: [""] + resources: [configmaps] + verbs: [get, list, watch, update, patch, delete] + # scoped to the configmap name + resourceNames: [argocd-operator-manager-config] +``` + +#### Putting it all together + +Once the installer service account required cluster-scoped and namespace-scoped permissions have been collected: +1. Create the installation namespace +2. Create the installer `ServiceAccount` +3. Create the installer `ClusterRole` +4. Create the `ClusterRoleBinding` between the installer service account and its cluster role +5. Create the installer `Role` +6. Create the `RoleBinding` between the installer service account and its role +7. Create the `ClusterExtension` + +A manifest with the full set of resources can be found [here](../../config/samples/olm_v1alpha1_clusterextension.yaml). + +### Alternatives + +We understand that manually determining the minimum RBAC required for installation/upgrade of a `ClusterExtension` quite complex and protracted. +In the near future, OLM v1 will provide tools and automation in order to simplify this process while maintaining our security posture. +For users wishing to test out OLM v1 in a non-production settings, we offer the following alternatives: + +#### Give the installer service account admin privileges + +The `cluster-admin` `ClusterRole` can be bound to the installer service account giving it full permissions to the cluster. +While this obviates the need to determine the minimal RBAC required for installation, it is also dangerous. It is highly recommended +that this alternative only be used in test clusters. Never in production. + +Below is an example ClusterRoleBinding using the cluster-admin ClusterRole: + +```terminal +# Create ClusterRole +kubectl apply -f - < Date: Wed, 2 Oct 2024 18:43:27 +0200 Subject: [PATCH 048/694] :seedling: Bump github.com/containers/common from 0.60.2 to 0.60.4 (#1325) Bumps [github.com/containers/common](https://github.com/containers/common) from 0.60.2 to 0.60.4. - [Release notes](https://github.com/containers/common/releases) - [Commits](https://github.com/containers/common/compare/v0.60.2...v0.60.4) --- updated-dependencies: - dependency-name: github.com/containers/common dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3b35708682..2af8e39b30 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect github.com/containerd/ttrpc v1.2.5 // indirect github.com/containerd/typeurl/v2 v2.1.1 // indirect - github.com/containers/common v0.60.2 // indirect + github.com/containers/common v0.60.4 // indirect github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect github.com/containers/ocicrypt v1.2.0 // indirect github.com/containers/storage v1.55.0 // indirect diff --git a/go.sum b/go.sum index 124f295ce2..adce30d7cf 100644 --- a/go.sum +++ b/go.sum @@ -103,8 +103,8 @@ github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oL github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= -github.com/containers/common v0.60.2 h1:utcwp2YkO8c0mNlwRxsxfOiqfj157FRrBjxgjR6f+7o= -github.com/containers/common v0.60.2/go.mod h1:I0upBi1qJX3QmzGbUOBN1LVP6RvkKhd3qQpZbQT+Q54= +github.com/containers/common v0.60.4 h1:H5+LAMHPZEqX6vVNOQ+IguVsaFl8kbO/SZ/VPXjxhy0= +github.com/containers/common v0.60.4/go.mod h1:I0upBi1qJX3QmzGbUOBN1LVP6RvkKhd3qQpZbQT+Q54= github.com/containers/image/v5 v5.32.2 h1:SzNE2Y6sf9b1GJoC8qjCuMBXwQrACFp4p0RK15+4gmQ= github.com/containers/image/v5 v5.32.2/go.mod h1:v1l73VeMugfj/QtKI+jhYbwnwFCFnNGckvbST3rQ5Hk= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= From ab996420d777800b27e6f35872b8ce26a840b8cf Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Thu, 3 Oct 2024 17:16:25 +0200 Subject: [PATCH 049/694] Replace `require` with `assert` in retry funcs (#1330) When `require` assertions are not satisfied they fails a test immediately. This is not desired when used with retry function such as `EventuallyWithT` since they are supposed to retry on a failure. This can cause flakes when, for example, we are asserting on a resource condition. On the first hit the resource might not have a condition and `require` will fail `EventuallyWithT` despite the fact that the code is correct and the condition gets set eventually. Signed-off-by: Mikalai Radchuk --- test/e2e/cluster_extension_install_test.go | 80 ++++++++++++++++------ 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 1e32166cbe..f3f9d230e4 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -274,7 +274,9 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -283,7 +285,9 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") @@ -327,7 +331,9 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [operatorhubio test-catalog]") @@ -372,7 +378,9 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { ) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -391,7 +399,9 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"prometheus\" matching version \"1.2.0\"", cond.Message) }, pollDuration, pollInterval) @@ -426,7 +436,9 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -441,7 +453,9 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -475,7 +489,9 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -489,7 +505,9 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -533,7 +551,9 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -546,7 +566,9 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeServing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) }, pollDuration, pollInterval) @@ -555,7 +577,9 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -611,7 +635,9 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -624,7 +650,9 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeServing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) }, pollDuration, pollInterval) @@ -633,7 +661,9 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) @@ -671,7 +701,9 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") @@ -740,7 +772,9 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) }, pollDuration, pollInterval) @@ -749,7 +783,9 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonFailed, cond.Reason) assert.Contains(ct, cond.Message, "forbidden") @@ -766,7 +802,9 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") @@ -777,7 +815,9 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - require.NotNil(ct, cond) + if !assert.NotNil(ct, cond) { + return + } assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) From 117becec1bfbb6227f20061fa05343961e1ac7ee Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Thu, 3 Oct 2024 19:32:32 +0200 Subject: [PATCH 050/694] Refactor `catalogmetadata` client and cache (#1318) It is now possible to cache errors occured on the cache population. This will be useful when we start proactively populating cache from `ClusterCatalogReconciler` instead of doing this on-demand. This refactoring also moves the responsibility of performing network requests out of the cache backend into the client. Signed-off-by: Mikalai Radchuk --- cmd/manager/main.go | 6 +- internal/catalogmetadata/cache/cache.go | 156 ++++---- internal/catalogmetadata/cache/cache_test.go | 342 +++++------------- internal/catalogmetadata/client/client.go | 125 ++++++- .../catalogmetadata/client/client_test.go | 273 ++++++++++++-- 5 files changed, 499 insertions(+), 403 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 72465b4dd8..947fca826c 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -222,10 +222,10 @@ func main() { setupLog.Error(err, "unable to create catalogs cache directory") os.Exit(1) } - cacheFetcher := cache.NewFilesystemCache(catalogsCachePath, func() (*http.Client, error) { + catalogClientBackend := cache.NewFilesystemCache(catalogsCachePath) + catalogClient := catalogclient.New(catalogClientBackend, func() (*http.Client, error) { return httputil.BuildHTTPClient(certPoolWatcher) }) - catalogClient := catalogclient.New(cacheFetcher) resolver := &resolve.CatalogResolver{ WalkCatalogsFunc: resolve.CatalogWalker( @@ -284,7 +284,7 @@ func main() { if err = (&controllers.ClusterCatalogReconciler{ Client: cl, - Cache: cacheFetcher, + Cache: catalogClientBackend, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ClusterCatalog") os.Exit(1) diff --git a/internal/catalogmetadata/cache/cache.go b/internal/catalogmetadata/cache/cache.go index e986c9714e..70dbe51f87 100644 --- a/internal/catalogmetadata/cache/cache.go +++ b/internal/catalogmetadata/cache/cache.go @@ -1,35 +1,24 @@ package cache import ( - "context" "fmt" + "io" "io/fs" - "net/http" "os" "path/filepath" "sync" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-controller/internal/catalogmetadata/client" ) -var _ client.Fetcher = &filesystemCache{} - -// NewFilesystemCache returns a client.Fetcher implementation that uses a -// local filesystem to cache Catalog contents. When fetching the Catalog contents -// it will: -// - Check if the Catalog is cached -// - IF !cached it will fetch from the catalogd HTTP server and cache the response -// - IF cached it will verify the cache is up to date. If it is up to date it will return -// the cached contents, if not it will fetch the new contents from the catalogd HTTP -// server and update the cached contents. -func NewFilesystemCache(cachePath string, clientFunc func() (*http.Client, error)) *filesystemCache { +var _ client.Cache = &filesystemCache{} + +func NewFilesystemCache(cachePath string) *filesystemCache { return &filesystemCache{ cachePath: cachePath, mutex: sync.RWMutex{}, - getClient: clientFunc, cacheDataByCatalogName: map[string]cacheData{}, } } @@ -40,75 +29,34 @@ func NewFilesystemCache(cachePath string, clientFunc func() (*http.Client, error // the cache. type cacheData struct { ResolvedRef string + Error error } // FilesystemCache is a cache that // uses the local filesystem for caching -// catalog contents. It will fetch catalog -// contents if the catalog does not already -// exist in the cache. +// catalog contents. type filesystemCache struct { mutex sync.RWMutex cachePath string - getClient func() (*http.Client, error) cacheDataByCatalogName map[string]cacheData } -// FetchCatalogContents implements the client.Fetcher interface and -// will fetch the contents for the provided Catalog from the filesystem. -// If the provided Catalog has not yet been cached, it will make a GET -// request to the Catalogd HTTP server to get the Catalog contents and cache -// them. The cache will be updated automatically if a Catalog is noticed to -// have a different resolved image reference. -// The Catalog provided to this function is expected to: -// - Be non-nil -// - Have a non-nil Catalog.Status.ResolvedSource.Image -// This ensures that we are only attempting to fetch catalog contents for Catalog -// resources that have been successfully reconciled, unpacked, and are being served. -// These requirements help ensure that we can rely on status conditions to determine -// when to issue a request to update the cached Catalog contents. -func (fsc *filesystemCache) FetchCatalogContents(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) { - if catalog == nil { - return nil, fmt.Errorf("error: provided catalog must be non-nil") - } - - if catalog.Status.ResolvedSource == nil { - return nil, fmt.Errorf("error: catalog %q has a nil status.resolvedSource value", catalog.Name) - } - - if catalog.Status.ResolvedSource.Image == nil { - return nil, fmt.Errorf("error: catalog %q has a nil status.resolvedSource.image value", catalog.Name) - } - - cacheDir := fsc.cacheDir(catalog.Name) - fsc.mutex.RLock() - if data, ok := fsc.cacheDataByCatalogName[catalog.Name]; ok { - if catalog.Status.ResolvedSource.Image.ResolvedRef == data.ResolvedRef { - fsc.mutex.RUnlock() - return os.DirFS(cacheDir), nil - } - } - fsc.mutex.RUnlock() - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, catalog.Status.ContentURL, nil) - if err != nil { - return nil, fmt.Errorf("error forming request: %v", err) - } - - client, err := fsc.getClient() - if err != nil { - return nil, fmt.Errorf("error getting HTTP client: %w", err) - } - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error performing request: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("error: received unexpected response status code %d", resp.StatusCode) - } - +// Put writes content from source to the filesystem and stores errToCache +// for a specified catalog name and version (resolvedRef). +// +// Method behaviour is as follows: +// - If successfully populated cache for catalogName and resolvedRef exists, +// errToCache is ignored and existing cache returned with nil error +// - If existing cache for catalogName and resolvedRef exists but +// is populated with an error, update the cache with either +// new content from source or errToCache. +// - If cache doesn't exist, populate it with either new content +// from source or errToCache. +// +// This cache implementation tracks only one version of cache per catalog, +// so Put will override any existing cache on the filesystem for catalogName +// if resolvedRef does not match the one which is already tracked. +func (fsc *filesystemCache) Put(catalogName, resolvedRef string, source io.Reader, errToCache error) (fs.FS, error) { fsc.mutex.Lock() defer fsc.mutex.Unlock() @@ -117,19 +65,35 @@ func (fsc *filesystemCache) FetchCatalogContents(ctx context.Context, catalog *c // updating this, has no way to tell if the current ref is the // newest possible ref. If another thread has already updated // this to be the same value, skip the write logic and return - // the cached contents - if data, ok := fsc.cacheDataByCatalogName[catalog.Name]; ok { - if data.ResolvedRef == catalog.Status.ResolvedSource.Image.ResolvedRef { - return os.DirFS(cacheDir), nil - } + // the cached contents. + if cache, err := fsc.get(catalogName, resolvedRef); err == nil && cache != nil { + // We only return here if the was no error during + // the previous (likely concurrent) cache population attempt. + // If there was an error - we want to try and populate the cache again. + return cache, nil + } + + var cacheFS fs.FS + if errToCache == nil { + cacheFS, errToCache = fsc.writeFS(catalogName, source) } + fsc.cacheDataByCatalogName[catalogName] = cacheData{ + ResolvedRef: resolvedRef, + Error: errToCache, + } + + return cacheFS, errToCache +} + +func (fsc *filesystemCache) writeFS(catalogName string, source io.Reader) (fs.FS, error) { + cacheDir := fsc.cacheDir(catalogName) - tmpDir, err := os.MkdirTemp(fsc.cachePath, fmt.Sprintf(".%s-", catalog.Name)) + tmpDir, err := os.MkdirTemp(fsc.cachePath, fmt.Sprintf(".%s-", catalogName)) if err != nil { return nil, fmt.Errorf("error creating temporary directory to unpack catalog metadata: %v", err) } - if err := declcfg.WalkMetasReader(resp.Body, func(meta *declcfg.Meta, err error) error { + if err := declcfg.WalkMetasReader(source, func(meta *declcfg.Meta, err error) error { if err != nil { return fmt.Errorf("error parsing catalog contents: %v", err) } @@ -160,11 +124,35 @@ func (fsc *filesystemCache) FetchCatalogContents(ctx context.Context, catalog *c return nil, fmt.Errorf("error moving temporary directory to cache directory: %v", err) } - fsc.cacheDataByCatalogName[catalog.Name] = cacheData{ - ResolvedRef: catalog.Status.ResolvedSource.Image.ResolvedRef, + return os.DirFS(cacheDir), nil +} + +// Get returns cache for a specified catalog name and version (resolvedRef). +// +// Method behaviour is as follows: +// - If cache exists, it returns a non-nil fs.FS and nil error +// - If cache doesn't exist, it returns nil fs.FS and nil error +// - If there was an error during cache population, +// it returns nil fs.FS and the error from the cache population. +// In other words - cache population errors are also cached. +func (fsc *filesystemCache) Get(catalogName, resolvedRef string) (fs.FS, error) { + fsc.mutex.RLock() + defer fsc.mutex.RUnlock() + return fsc.get(catalogName, resolvedRef) +} + +func (fsc *filesystemCache) get(catalogName, resolvedRef string) (fs.FS, error) { + cacheDir := fsc.cacheDir(catalogName) + if data, ok := fsc.cacheDataByCatalogName[catalogName]; ok { + if resolvedRef == data.ResolvedRef { + if data.Error != nil { + return nil, data.Error + } + return os.DirFS(cacheDir), nil + } } - return os.DirFS(cacheDir), nil + return nil, nil } // Remove deletes cache directory for a given catalog from the filesystem diff --git a/internal/catalogmetadata/cache/cache_test.go b/internal/catalogmetadata/cache/cache_test.go index 74f7d79c0a..f898e23e32 100644 --- a/internal/catalogmetadata/cache/cache_test.go +++ b/internal/catalogmetadata/cache/cache_test.go @@ -2,28 +2,22 @@ package cache_test import ( "bytes" - "context" "encoding/json" "errors" "fmt" "io" "io/fs" - "maps" - "net/http" "os" "path/filepath" + "strings" "testing" "testing/fstest" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" - "github.com/operator-framework/operator-registry/alpha/declcfg" - "github.com/operator-framework/operator-controller/internal/catalogmetadata/cache" ) @@ -58,233 +52,105 @@ const ( }` ) -var defaultFS = fstest.MapFS{ - "fake1/olm.package/fake1.json": &fstest.MapFile{Data: []byte(package1)}, - "fake1/olm.bundle/fake1.v1.0.0.json": &fstest.MapFile{Data: []byte(bundle1)}, - "fake1/olm.channel/stable.json": &fstest.MapFile{Data: []byte(stableChannel)}, +func defaultContent() io.Reader { + return strings.NewReader(package1 + bundle1 + stableChannel) } -func TestFilesystemCacheFetchCatalogContents(t *testing.T) { - type test struct { - name string - catalog *catalogd.ClusterCatalog - contents fstest.MapFS - wantErr bool - tripper *mockTripper - testCaching bool - shouldHitCache bool +func defaultFS() fstest.MapFS { + return fstest.MapFS{ + "fake1/olm.package/fake1.json": &fstest.MapFile{Data: []byte(package1)}, + "fake1/olm.bundle/fake1.v1.0.0.json": &fstest.MapFile{Data: []byte(bundle1)}, + "fake1/olm.channel/stable.json": &fstest.MapFile{Data: []byte(stableChannel)}, } - for _, tt := range []test{ - { - name: "valid non-cached fetch", - catalog: &catalogd.ClusterCatalog{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-catalog", - }, - Status: catalogd.ClusterCatalogStatus{ - ResolvedSource: &catalogd.ResolvedCatalogSource{ - Type: catalogd.SourceTypeImage, - Image: &catalogd.ResolvedImageSource{ - ResolvedRef: "fake/catalog@sha256:fakesha", - }, - }, - }, - }, - contents: defaultFS, - tripper: &mockTripper{}, - }, - { - name: "valid cached fetch", - catalog: &catalogd.ClusterCatalog{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-catalog", - }, - Status: catalogd.ClusterCatalogStatus{ - ResolvedSource: &catalogd.ResolvedCatalogSource{ - Type: catalogd.SourceTypeImage, - Image: &catalogd.ResolvedImageSource{ - ResolvedRef: "fake/catalog@sha256:fakesha", - }, - }, - }, - }, - contents: defaultFS, - tripper: &mockTripper{}, - testCaching: true, - shouldHitCache: true, - }, - { - name: "cached update fetch with changes", - catalog: &catalogd.ClusterCatalog{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-catalog", - }, - Status: catalogd.ClusterCatalogStatus{ - ResolvedSource: &catalogd.ResolvedCatalogSource{ - Type: catalogd.SourceTypeImage, - Image: &catalogd.ResolvedImageSource{ - ResolvedRef: "fake/catalog@sha256:fakesha", - }, - }, - }, - }, - contents: defaultFS, - tripper: &mockTripper{}, - testCaching: true, - shouldHitCache: false, - }, - { - name: "fetch error", - catalog: &catalogd.ClusterCatalog{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-catalog", - }, - Status: catalogd.ClusterCatalogStatus{ - ResolvedSource: &catalogd.ResolvedCatalogSource{ - Type: catalogd.SourceTypeImage, - Image: &catalogd.ResolvedImageSource{ - ResolvedRef: "fake/catalog@sha256:fakesha", - }, - }, - }, - }, - contents: defaultFS, - tripper: &mockTripper{shouldError: true}, - wantErr: true, - }, - { - name: "fetch internal server error response", - catalog: &catalogd.ClusterCatalog{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-catalog", - }, - Status: catalogd.ClusterCatalogStatus{ - ResolvedSource: &catalogd.ResolvedCatalogSource{ - Type: catalogd.SourceTypeImage, - Image: &catalogd.ResolvedImageSource{ - ResolvedRef: "fake/catalog@sha256:fakesha", - }, - }, - }, - }, - contents: defaultFS, - tripper: &mockTripper{serverError: true}, - wantErr: true, - }, - { - name: "nil catalog", - catalog: nil, - contents: defaultFS, - tripper: &mockTripper{serverError: true}, - wantErr: true, - }, - { - name: "nil catalog.status.resolvedSource", - catalog: &catalogd.ClusterCatalog{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-catalog", - }, - Status: catalogd.ClusterCatalogStatus{ - ResolvedSource: nil, - }, - }, - contents: defaultFS, - tripper: &mockTripper{serverError: true}, - wantErr: true, - }, - { - name: "nil catalog.status.resolvedSource.image", - catalog: &catalogd.ClusterCatalog{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-catalog", - }, - Status: catalogd.ClusterCatalogStatus{ - ResolvedSource: &catalogd.ResolvedCatalogSource{ - Image: nil, - }, - }, - }, - contents: defaultFS, - tripper: &mockTripper{serverError: true}, - wantErr: true, - }, - } { - t.Run(tt.name, func(t *testing.T) { - ctx := context.Background() - cacheDir := t.TempDir() - tt.tripper.content = make(fstest.MapFS) - maps.Copy(tt.tripper.content, tt.contents) - httpClient := &http.Client{ - Transport: tt.tripper, - } - c := cache.NewFilesystemCache(cacheDir, func() (*http.Client, error) { - return httpClient, nil - }) - - actualFS, err := c.FetchCatalogContents(ctx, tt.catalog) - if !tt.wantErr { - assert.NoError(t, err) - assert.NoError(t, equalFilesystems(tt.contents, actualFS)) - } else { - assert.Error(t, err) - } +} - if tt.testCaching { - if !tt.shouldHitCache { - tt.catalog.Status.ResolvedSource.Image.ResolvedRef = "fake/catalog@sha256:shafake" - } - tt.tripper.content["foobar/olm.package/foobar.json"] = &fstest.MapFile{Data: []byte(`{"schema": "olm.package", "name": "foobar"}`)} - actualFS, err := c.FetchCatalogContents(ctx, tt.catalog) - assert.NoError(t, err) - if !tt.shouldHitCache { - assert.NoError(t, equalFilesystems(tt.tripper.content, actualFS)) - assert.ErrorContains(t, equalFilesystems(tt.contents, actualFS), "foobar/olm.package/foobar.json") - } else { - assert.NoError(t, equalFilesystems(tt.contents, actualFS)) - } - } - }) - } +func TestFilesystemCachePutAndGet(t *testing.T) { + const ( + catalogName = "test-catalog" + resolvedRef1 = "fake/catalog@sha256:fakesha1" + resolvedRef2 = "fake/catalog@sha256:fakesha2" + ) + + cacheDir := t.TempDir() + c := cache.NewFilesystemCache(cacheDir) + + catalogCachePath := filepath.Join(cacheDir, catalogName) + require.NoDirExists(t, catalogCachePath) + + t.Log("Get empty v1 cache") + actualFSGet, err := c.Get(catalogName, resolvedRef1) + assert.NoError(t, err) + assert.Nil(t, actualFSGet) + + t.Log("Put v1 content into cache") + actualFSPut, err := c.Put(catalogName, resolvedRef1, defaultContent(), nil) + assert.NoError(t, err) + require.NotNil(t, actualFSPut) + assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) + + t.Log("Get v1 content from cache") + actualFSGet, err = c.Get(catalogName, resolvedRef1) + assert.NoError(t, err) + require.NotNil(t, actualFSGet) + assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) + assert.NoError(t, equalFilesystems(actualFSPut, actualFSGet)) + + t.Log("Put v1 error into cache") + actualFSPut, err = c.Put(catalogName, resolvedRef1, nil, errors.New("fake put error")) + // Errors do not override previously successfully populated cache + assert.NoError(t, err) + require.NotNil(t, actualFSPut) + assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) + assert.NoError(t, equalFilesystems(actualFSPut, actualFSGet)) + + t.Log("Put v2 error into cache") + actualFSPut, err = c.Put(catalogName, resolvedRef2, nil, errors.New("fake v2 put error")) + assert.Equal(t, errors.New("fake v2 put error"), err) + assert.Nil(t, actualFSPut) + + t.Log("Get v2 error from cache") + actualFSGet, err = c.Get(catalogName, resolvedRef2) + assert.Equal(t, errors.New("fake v2 put error"), err) + assert.Nil(t, actualFSGet) + + t.Log("Put v2 content into cache") + actualFSPut, err = c.Put(catalogName, resolvedRef2, defaultContent(), nil) + assert.NoError(t, err) + require.NotNil(t, actualFSPut) + assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) + + t.Log("Get v2 content from cache") + actualFSGet, err = c.Get(catalogName, resolvedRef2) + assert.NoError(t, err) + require.NotNil(t, actualFSGet) + assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) + assert.NoError(t, equalFilesystems(actualFSPut, actualFSGet)) + + t.Log("Get empty v1 cache") + // Cache should be empty and no error because + // Put with a new version overrides the old version + actualFSGet, err = c.Get(catalogName, resolvedRef1) + assert.NoError(t, err) + assert.Nil(t, actualFSGet) } func TestFilesystemCacheRemove(t *testing.T) { - testCatalog := &catalogd.ClusterCatalog{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-catalog", - }, - Status: catalogd.ClusterCatalogStatus{ - ResolvedSource: &catalogd.ResolvedCatalogSource{ - Type: catalogd.SourceTypeImage, - Image: &catalogd.ResolvedImageSource{ - ResolvedRef: "fake/catalog@sha256:fakesha", - }, - }, - }, - } + catalogName := "test-catalog" + resolvedRef := "fake/catalog@sha256:fakesha" - ctx := context.Background() cacheDir := t.TempDir() + c := cache.NewFilesystemCache(cacheDir) - tripper := &mockTripper{} - tripper.content = make(fstest.MapFS) - maps.Copy(tripper.content, defaultFS) - httpClient := &http.Client{ - Transport: tripper, - } - c := cache.NewFilesystemCache(cacheDir, func() (*http.Client, error) { - return httpClient, nil - }) - - catalogCachePath := filepath.Join(cacheDir, testCatalog.Name) + catalogCachePath := filepath.Join(cacheDir, catalogName) t.Log("Remove cache before it exists") require.NoDirExists(t, catalogCachePath) - err := c.Remove(testCatalog.Name) + err := c.Remove(catalogName) require.NoError(t, err) assert.NoDirExists(t, catalogCachePath) t.Log("Fetch contents to populate cache") - _, err = c.FetchCatalogContents(ctx, testCatalog) + _, err = c.Put(catalogName, resolvedRef, defaultContent(), nil) require.NoError(t, err) require.DirExists(t, catalogCachePath) @@ -292,7 +158,7 @@ func TestFilesystemCacheRemove(t *testing.T) { require.NoError(t, os.Chmod(catalogCachePath, 0000)) t.Log("Remove cache causes an error") - err = c.Remove(testCatalog.Name) + err = c.Remove(catalogName) require.ErrorContains(t, err, "error removing cache directory") require.DirExists(t, catalogCachePath) @@ -300,49 +166,11 @@ func TestFilesystemCacheRemove(t *testing.T) { require.NoError(t, os.Chmod(catalogCachePath, 0777)) t.Log("Remove cache") - err = c.Remove(testCatalog.Name) + err = c.Remove(catalogName) require.NoError(t, err) assert.NoDirExists(t, catalogCachePath) } -var _ http.RoundTripper = &mockTripper{} - -type mockTripper struct { - content fstest.MapFS - shouldError bool - serverError bool -} - -func (mt *mockTripper) RoundTrip(_ *http.Request) (*http.Response, error) { - if mt.shouldError { - return nil, errors.New("mock tripper error") - } - - if mt.serverError { - return &http.Response{ - StatusCode: http.StatusInternalServerError, - Body: http.NoBody, - }, nil - } - - pr, pw := io.Pipe() - - go func() { - _ = pw.CloseWithError(declcfg.WalkMetasFS(context.Background(), mt.content, func(_ string, meta *declcfg.Meta, err error) error { - if err != nil { - return err - } - _, err = pw.Write(meta.Blob) - return err - })) - }() - - return &http.Response{ - StatusCode: http.StatusOK, - Body: pr, - }, nil -} - func equalFilesystems(expected, actual fs.FS) error { normalizeJSON := func(data []byte) []byte { var v interface{} diff --git a/internal/catalogmetadata/client/client.go b/internal/catalogmetadata/client/client.go index 6a9efe2858..50033fdb21 100644 --- a/internal/catalogmetadata/client/client.go +++ b/internal/catalogmetadata/client/client.go @@ -4,7 +4,9 @@ import ( "context" "errors" "fmt" + "io" "io/fs" + "net/http" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -13,39 +15,62 @@ import ( "github.com/operator-framework/operator-registry/alpha/declcfg" ) -// Fetcher is an interface to facilitate fetching -// catalog contents from catalogd. -type Fetcher interface { - // FetchCatalogContents fetches contents from the catalogd HTTP - // server for the catalog provided. It returns a fs.FS containing the FBC contents. - // Each sub directory contains FBC for a single package - // and the directory name is package name. - // Returns an error if any occur. - FetchCatalogContents(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) +type Cache interface { + // Get returns cache for a specified catalog name and version (resolvedRef). + // + // Method behaviour is as follows: + // - If cache exists, it returns a non-nil fs.FS and nil error + // - If cache doesn't exist, it returns nil fs.FS and nil error + // - If there was an error during cache population, + // it returns nil fs.FS and the error from the cache population. + // In other words - cache population errors are also cached. + Get(catalogName, resolvedRef string) (fs.FS, error) + + // Put writes content from source or from errToCache in the cache backend + // for a specified catalog name and version (resolvedRef). + // + // Method behaviour is as follows: + // - If successfully populated cache for catalogName and resolvedRef exists, + // errToCache is ignored and existing cache returned with nil error + // - If existing cache for catalogName and resolvedRef exists but + // is populated with an error, update the cache with either + // new content from source or errToCache. + // - If cache doesn't exist, populate it with either new content + // from source or errToCache. + Put(catalogName, resolvedRef string, source io.Reader, errToCache error) (fs.FS, error) } -func New(fetcher Fetcher) *Client { +func New(cache Cache, httpClient func() (*http.Client, error)) *Client { return &Client{ - fetcher: fetcher, + cache: cache, + httpClient: httpClient, } } // Client is reading catalog metadata type Client struct { - // fetcher is the Fetcher to use for fetching catalog contents - fetcher Fetcher + cache Cache + httpClient func() (*http.Client, error) } func (c *Client) GetPackage(ctx context.Context, catalog *catalogd.ClusterCatalog, pkgName string) (*declcfg.DeclarativeConfig, error) { - // if the catalog has not been successfully unpacked, report an error. This ensures that our - // reconciles are deterministic and wait for all desired catalogs to be ready. - if !meta.IsStatusConditionPresentAndEqual(catalog.Status.Conditions, catalogd.TypeServing, metav1.ConditionTrue) { - return nil, fmt.Errorf("catalog %q is not being served", catalog.Name) + if err := validateCatalog(catalog); err != nil { + return nil, err } - catalogFsys, err := c.fetcher.FetchCatalogContents(ctx, catalog) + catalogFsys, err := c.cache.Get(catalog.Name, catalog.Status.ResolvedSource.Image.ResolvedRef) if err != nil { - return nil, fmt.Errorf("error fetching catalog contents: %v", err) + return nil, fmt.Errorf("error retrieving catalog cache: %v", err) + } + if catalogFsys == nil { + // TODO: https://github.com/operator-framework/operator-controller/pull/1284 + // For now we are still populating cache (if absent) on-demand, + // but we might end up just returning a "cache not found" error here + // once we implement cache population in the controller. + catalogFsys, err = c.PopulateCache(ctx, catalog) + if err != nil { + return nil, fmt.Errorf("error fetching catalog contents: %v", err) + } } pkgFsys, err := fs.Sub(catalogFsys, pkgName) @@ -65,3 +90,65 @@ func (c *Client) GetPackage(ctx context.Context, catalog *catalogd.ClusterCatalo } return pkgFBC, nil } + +func (c *Client) PopulateCache(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) { + if err := validateCatalog(catalog); err != nil { + return nil, err + } + + resp, err := c.doRequest(ctx, catalog) + if err != nil { + // Any errors from the http request we want to cache + // so later on cache get they can be bubbled up to the user. + return c.cache.Put(catalog.Name, catalog.Status.ResolvedSource.Image.ResolvedRef, nil, err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + errToCache := fmt.Errorf("error: received unexpected response status code %d", resp.StatusCode) + return c.cache.Put(catalog.Name, catalog.Status.ResolvedSource.Image.ResolvedRef, nil, errToCache) + } + + return c.cache.Put(catalog.Name, catalog.Status.ResolvedSource.Image.ResolvedRef, resp.Body, nil) +} + +func (c *Client) doRequest(ctx context.Context, catalog *catalogd.ClusterCatalog) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, catalog.Status.ContentURL, nil) + if err != nil { + return nil, fmt.Errorf("error forming request: %v", err) + } + + client, err := c.httpClient() + if err != nil { + return nil, fmt.Errorf("error getting HTTP client: %v", err) + } + + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing request: %v", err) + } + + return resp, nil +} + +func validateCatalog(catalog *catalogd.ClusterCatalog) error { + if catalog == nil { + return fmt.Errorf("error: provided catalog must be non-nil") + } + + // if the catalog has not been successfully unpacked, report an error. This ensures that our + // reconciles are deterministic and wait for all desired catalogs to be ready. + if !meta.IsStatusConditionPresentAndEqual(catalog.Status.Conditions, catalogd.TypeServing, metav1.ConditionTrue) { + return fmt.Errorf("catalog %q is not being served", catalog.Name) + } + + if catalog.Status.ResolvedSource == nil { + return fmt.Errorf("error: catalog %q has a nil status.resolvedSource value", catalog.Name) + } + + if catalog.Status.ResolvedSource.Image == nil { + return fmt.Errorf("error: catalog %q has a nil status.resolvedSource.image value", catalog.Name) + } + + return nil +} diff --git a/internal/catalogmetadata/client/client_test.go b/internal/catalogmetadata/client/client_test.go index 989b66acc0..a1cec5d4ae 100644 --- a/internal/catalogmetadata/client/client_test.go +++ b/internal/catalogmetadata/client/client_test.go @@ -3,7 +3,10 @@ package client_test import ( "context" "errors" + "io" "io/fs" + "net/http" + "strings" "testing" "testing/fstest" @@ -16,99 +19,289 @@ import ( catalogClient "github.com/operator-framework/operator-controller/internal/catalogmetadata/client" ) -func TestClientNew(t *testing.T) { +func defaultCatalog() *catalogd.ClusterCatalog { + return &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{Name: "catalog-1"}, + Status: catalogd.ClusterCatalogStatus{ + Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}, + ResolvedSource: &catalogd.ResolvedCatalogSource{Image: &catalogd.ResolvedImageSource{ + ResolvedRef: "fake/catalog@sha256:fakesha", + }}, + ContentURL: "https://fake-url.svc.local/all.json", + }, + } +} + +func TestClientGetPackage(t *testing.T) { testFS := fstest.MapFS{ "pkg-present/olm.package/pkg-present.json": &fstest.MapFile{Data: []byte(`{"schema": "olm.package","name": "pkg-present"}`)}, } type testCase struct { name string - catalog *catalogd.ClusterCatalog + catalog func() *catalogd.ClusterCatalog pkgName string - fetcher catalogClient.Fetcher + cache catalogClient.Cache assert func(*testing.T, *declcfg.DeclarativeConfig, error) } for _, tc := range []testCase{ { - name: "not unpacked", - catalog: &catalogd.ClusterCatalog{ObjectMeta: metav1.ObjectMeta{Name: "catalog-1"}}, - fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return testFS, nil }), + name: "not unpacked", + catalog: func() *catalogd.ClusterCatalog { + return &catalogd.ClusterCatalog{ObjectMeta: metav1.ObjectMeta{Name: "catalog-1"}} + }, assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { assert.ErrorContains(t, err, `catalog "catalog-1" is not being served`) }, }, { - name: "unpacked, fetcher returns error", - catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, - }, - fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return nil, errors.New("fetch error") }), + name: "unpacked, cache returns error", + catalog: defaultCatalog, + cache: &fakeCache{getErr: errors.New("fetch error")}, assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { - assert.ErrorContains(t, err, `error fetching catalog contents: fetch error`) + assert.ErrorContains(t, err, `error retrieving catalog cache`) }, }, { - name: "unpacked, invalid package path", - catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, - }, - fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return testFS, nil }), + name: "unpacked, invalid package path", + catalog: defaultCatalog, + cache: &fakeCache{getFS: testFS}, pkgName: "/", assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { assert.ErrorContains(t, err, `error getting package "/"`) }, }, { - name: "unpacked, package missing", - catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, - }, + name: "unpacked, package missing", + catalog: defaultCatalog, pkgName: "pkg-missing", - fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return testFS, nil }), + cache: &fakeCache{getFS: testFS}, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { assert.NoError(t, err) assert.Equal(t, &declcfg.DeclarativeConfig{}, fbc) }, }, { - name: "unpacked, invalid package present", - catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, - }, + name: "unpacked, invalid package present", + catalog: defaultCatalog, pkgName: "invalid-pkg-present", - fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { - return fstest.MapFS{ - "invalid-pkg-present/olm.package/invalid-pkg-present.json": &fstest.MapFile{Data: []byte(`{"schema": "olm.package","name": 12345}`)}, - }, nil - }), + cache: &fakeCache{getFS: fstest.MapFS{ + "invalid-pkg-present/olm.package/invalid-pkg-present.json": &fstest.MapFile{Data: []byte(`{"schema": "olm.package","name": 12345}`)}, + }}, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { assert.ErrorContains(t, err, `error loading package "invalid-pkg-present"`) assert.Nil(t, fbc) }, }, { - name: "unpacked, package present", - catalog: &catalogd.ClusterCatalog{ - Status: catalogd.ClusterCatalogStatus{Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}}, + name: "unpacked, package present", + catalog: defaultCatalog, + pkgName: "pkg-present", + cache: &fakeCache{getFS: testFS}, + assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { + assert.NoError(t, err) + assert.Equal(t, &declcfg.DeclarativeConfig{Packages: []declcfg.Package{{Schema: declcfg.SchemaPackage, Name: "pkg-present"}}}, fbc) }, + }, + { + name: "cache unpopulated", + catalog: defaultCatalog, pkgName: "pkg-present", - fetcher: fetcherFunc(func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) { return testFS, nil }), + cache: &fakeCache{putFunc: func(source string, errToCache error) (fs.FS, error) { + return testFS, nil + }}, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { assert.NoError(t, err) assert.Equal(t, &declcfg.DeclarativeConfig{Packages: []declcfg.Package{{Schema: declcfg.SchemaPackage, Name: "pkg-present"}}}, fbc) }, }, + { + name: "cache unpopulated and fails to populate", + catalog: defaultCatalog, + pkgName: "pkg-present", + cache: &fakeCache{putErr: errors.New("fake cache put error")}, + assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { + assert.Nil(t, fbc) + assert.ErrorContains(t, err, "error fetching catalog contents") + }, + }, } { t.Run(tc.name, func(t *testing.T) { - c := catalogClient.New(tc.fetcher) - fbc, err := c.GetPackage(context.Background(), tc.catalog, tc.pkgName) + ctx := context.Background() + + c := catalogClient.New(tc.cache, func() (*http.Client, error) { + return &http.Client{ + // This is to prevent actual network calls + Transport: &fakeTripper{resp: &http.Response{ + StatusCode: http.StatusOK, + Body: http.NoBody, + }}, + }, nil + }) + fbc, err := c.GetPackage(ctx, tc.catalog(), tc.pkgName) tc.assert(t, fbc, err) }) } } -type fetcherFunc func(context.Context, *catalogd.ClusterCatalog) (fs.FS, error) +func TestClientPopulateCache(t *testing.T) { + testFS := fstest.MapFS{ + "pkg-present/olm.package/pkg-present.json": &fstest.MapFile{Data: []byte(`{"schema": "olm.package","name": "pkg-present"}`)}, + } + + type testCase struct { + name string + catalog func() *catalogd.ClusterCatalog + httpClient func() (*http.Client, error) + putFuncConstructor func(t *testing.T) func(source string, errToCache error) (fs.FS, error) + assert func(t *testing.T, fs fs.FS, err error) + } + for _, tt := range []testCase{ + { + name: "cache unpopulated, successful http request", + catalog: defaultCatalog, + httpClient: func() (*http.Client, error) { + return &http.Client{ + // This is to prevent actual network calls + Transport: &fakeTripper{resp: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader("fake-success-response-body")), + }}, + }, nil + }, + assert: func(t *testing.T, fs fs.FS, err error) { + assert.NoError(t, err) + assert.Equal(t, testFS, fs) + }, + putFuncConstructor: func(t *testing.T) func(source string, errToCache error) (fs.FS, error) { + return func(source string, errToCache error) (fs.FS, error) { + assert.Equal(t, "fake-success-response-body", source) + assert.NoError(t, errToCache) + return testFS, errToCache + } + }, + }, + { + name: "not unpacked", + catalog: func() *catalogd.ClusterCatalog { + return &catalogd.ClusterCatalog{ObjectMeta: metav1.ObjectMeta{Name: "catalog-1"}} + }, + assert: func(t *testing.T, fs fs.FS, err error) { + assert.Nil(t, fs) + assert.ErrorContains(t, err, `catalog "catalog-1" is not being served`) + }, + }, + { + name: "cache unpopulated, error on getting a http client", + catalog: defaultCatalog, + httpClient: func() (*http.Client, error) { + return nil, errors.New("fake error getting a http client") + }, + putFuncConstructor: func(t *testing.T) func(source string, errToCache error) (fs.FS, error) { + return func(source string, errToCache error) (fs.FS, error) { + assert.Empty(t, source) + assert.Error(t, errToCache) + return nil, errToCache + } + }, + assert: func(t *testing.T, fs fs.FS, err error) { + assert.Nil(t, fs) + assert.ErrorContains(t, err, "error getting HTTP client") + }, + }, + { + name: "cache unpopulated, error on http request", + catalog: defaultCatalog, + httpClient: func() (*http.Client, error) { + return &http.Client{ + // This is to prevent actual network calls + Transport: &fakeTripper{err: errors.New("fake error on a http request")}, + }, nil + }, + putFuncConstructor: func(t *testing.T) func(source string, errToCache error) (fs.FS, error) { + return func(source string, errToCache error) (fs.FS, error) { + assert.Empty(t, source) + assert.Error(t, errToCache) + return nil, errToCache + } + }, + assert: func(t *testing.T, fs fs.FS, err error) { + assert.Nil(t, fs) + assert.ErrorContains(t, err, "error performing request") + }, + }, + { + name: "cache unpopulated, unexpected http status", + catalog: defaultCatalog, + httpClient: func() (*http.Client, error) { + return &http.Client{ + // This is to prevent actual network calls + Transport: &fakeTripper{resp: &http.Response{ + StatusCode: http.StatusInternalServerError, + Body: io.NopCloser(strings.NewReader("fake-unexpected-code-response-body")), + }}, + }, nil + }, + putFuncConstructor: func(t *testing.T) func(source string, errToCache error) (fs.FS, error) { + return func(source string, errToCache error) (fs.FS, error) { + assert.Empty(t, source) + assert.Error(t, errToCache) + return nil, errToCache + } + }, + assert: func(t *testing.T, fs fs.FS, err error) { + assert.Nil(t, fs) + assert.ErrorContains(t, err, "received unexpected response status code 500") + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + + cache := &fakeCache{} + if tt.putFuncConstructor != nil { + cache.putFunc = tt.putFuncConstructor(t) + } + + c := catalogClient.New(cache, tt.httpClient) + fs, err := c.PopulateCache(ctx, tt.catalog()) + tt.assert(t, fs, err) + }) + } +} + +type fakeCache struct { + getFS fs.FS + getErr error + + putFunc func(source string, errToCache error) (fs.FS, error) + putErr error +} + +func (c *fakeCache) Get(catalogName, resolvedRef string) (fs.FS, error) { + return c.getFS, c.getErr +} + +func (c *fakeCache) Put(catalogName, resolvedRef string, source io.Reader, errToCache error) (fs.FS, error) { + if c.putFunc != nil { + buf := new(strings.Builder) + if source != nil { + io.Copy(buf, source) // nolint:errcheck + } + return c.putFunc(buf.String(), errToCache) + } + if c.putErr != nil { + return nil, c.putErr + } + + return nil, errors.New("unexpected error") +} + +type fakeTripper struct { + resp *http.Response + err error +} -func (f fetcherFunc) FetchCatalogContents(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) { - return f(ctx, catalog) +func (ft *fakeTripper) RoundTrip(*http.Request) (*http.Response, error) { + return ft.resp, ft.err } From 814bf63854624fd347cde93238dbfe5c95aad6cf Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Thu, 3 Oct 2024 21:18:07 +0200 Subject: [PATCH 051/694] :book: Content organization (#1324) * organize docs Signed-off-by: Per Goncalves da Silva * First stab at doc hierarchy Signed-off-by: Per Goncalves da Silva --------- Signed-off-by: Per Goncalves da Silva Co-authored-by: Per Goncalves da Silva --- CONTRIBUTING.md | 11 +- Makefile | 13 +- README.md | 128 ++---------------- .../catalogd-api-reference.md | 0 .../crd-ref-docs-gen-config.yaml | 0 .../operator-controller-api-reference.md | 0 docs/assets/logo.svg | 98 ++++++++++++++ .../controlling-catalog-selection.md | 14 +- docs/{refs => concepts}/crd-upgrade-safety.md | 0 .../single-owner-objects.md | 2 +- docs/{drafts => concepts}/upgrade-support.md | 5 + docs/{drafts => concepts}/version-ranges.md | 2 +- docs/contribute/contributing.md | 1 + docs/{drafts => contribute}/developer.md | 2 +- docs/css/extra.css | 10 ++ .../Tasks/create-installer-service-account.md | 3 - ...eferences-permission-enforcement-plugin.md | 13 -- docs/drafts/provided-serviceaccount.md | 31 ----- docs/drafts/refs/olmv1-limitations.md | 3 - docs/drafts/support-watchNamespaces.md | 24 ---- docs/getting-started/olmv1_getting_started.md | 115 ++++++++++++++++ docs/{refs => howto}/catalog-queries.md | 2 - .../derive-service-account.md} | 11 +- .../how-to-channel-based-upgrades.md | 2 +- docs/{drafts => howto}/how-to-pin-version.md | 4 +- .../how-to-version-range-upgrades.md | 4 +- .../how-to-z-stream-upgrades.md | 4 +- docs/index.md | 44 +++--- .../olmv1_architecture.md} | 8 +- docs/project/olmv1_community.md | 15 ++ .../olmv1_design_decisions.md} | 96 +++++++------ .../olmv1_limitations.md} | 9 +- docs/{ => project}/olmv1_roadmap.md | 5 +- .../add-catalog.md} | 7 +- .../downgrade-extension.md} | 4 + .../explore-available-content.md} | 11 +- .../install-extension.md} | 24 +++- .../uninstall-extension.md} | 7 +- .../upgrade-extension.md} | 17 ++- mkdocs.yml | 65 ++++++--- 40 files changed, 487 insertions(+), 327 deletions(-) rename docs/{refs/api => api-reference}/catalogd-api-reference.md (100%) rename docs/{refs/api => api-reference}/crd-ref-docs-gen-config.yaml (100%) rename docs/{refs/api => api-reference}/operator-controller-api-reference.md (100%) create mode 100644 docs/assets/logo.svg rename docs/{drafts => concepts}/controlling-catalog-selection.md (94%) rename docs/{refs => concepts}/crd-upgrade-safety.md (100%) rename docs/{drafts => concepts}/single-owner-objects.md (100%) rename docs/{drafts => concepts}/upgrade-support.md (99%) rename docs/{drafts => concepts}/version-ranges.md (98%) create mode 120000 docs/contribute/contributing.md rename docs/{drafts => contribute}/developer.md (98%) create mode 100644 docs/css/extra.css delete mode 100644 docs/drafts/Tasks/create-installer-service-account.md delete mode 100644 docs/drafts/permissions-for-owner-references-permission-enforcement-plugin.md delete mode 100644 docs/drafts/provided-serviceaccount.md delete mode 100644 docs/drafts/refs/olmv1-limitations.md delete mode 100644 docs/drafts/support-watchNamespaces.md create mode 100644 docs/getting-started/olmv1_getting_started.md rename docs/{refs => howto}/catalog-queries.md (99%) rename docs/{drafts/derive-serviceaccount.md => howto/derive-service-account.md} (96%) rename docs/{drafts => howto}/how-to-channel-based-upgrades.md (96%) rename docs/{drafts => howto}/how-to-pin-version.md (87%) rename docs/{drafts => howto}/how-to-version-range-upgrades.md (91%) rename docs/{drafts => howto}/how-to-z-stream-upgrades.md (90%) rename docs/{drafts/architecture.md => project/olmv1_architecture.md} (98%) create mode 100644 docs/project/olmv1_community.md rename docs/{olmv1_overview.md => project/olmv1_design_decisions.md} (71%) rename docs/{refs/supported-extensions.md => project/olmv1_limitations.md} (85%) rename docs/{ => project}/olmv1_roadmap.md (99%) rename docs/{Tasks/adding-a-catalog.md => tutorials/add-catalog.md} (98%) rename docs/{drafts/downgrading-an-extension.md => tutorials/downgrade-extension.md} (99%) rename docs/{Tasks/exploring-available-packages.md => tutorials/explore-available-content.md} (96%) rename docs/{Tasks/installing-an-extension.md => tutorials/install-extension.md} (80%) rename docs/{Tasks/uninstalling-an-extension.md => tutorials/uninstall-extension.md} (94%) rename docs/{drafts/Tasks/upgrading-an-extension.md => tutorials/upgrade-extension.md} (94%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 78a858d256..dbd4508d3e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,15 +7,16 @@ Operator Controller is an Apache 2.0 licensed project and accepts contributions By contributing to this project you agree to the Developer Certificate of Origin (DCO). This document was created by the Linux Kernel community and is a simple statement that you, as a contributor, have the legal right to make the -contribution. See the [DCO](DCO) file for details. +contribution. See the [DCO](https://github.com/operator-framework/operator-controller/blob/main/DCO) file for details. ## Overview Thank you for your interest in contributing to the Operator-Controller. -As you may or may not know, the Operator-Controller project aims to deliver the user experience described in the [Operator Lifecycle Manager (OLM) V1 Product Requirements Document (PRD)](https://docs.google.com/document/d/1-vsZ2dAODNfoHb7Nf0fbYeKDF7DUqEzS9HqgeMCvbDs/edit). The design requirements captured in the OLM V1 PRD were born from customer and community feedback based on the experience they had with the released version of [OLM V0](github.com/operator-framework/operator-lifecycle-manager). +As you may or may not know, the Operator-Controller project aims to deliver the user experience described in the [Operator Lifecycle Manager (OLM) V1 Product Requirements Document (PRD)](https://docs.google.com/document/d/1-vsZ2dAODNfoHb7Nf0fbYeKDF7DUqEzS9HqgeMCvbDs/edit). The design requirements captured in the OLM V1 PRD were born from customer and community feedback based on the experience they had with the released version of [OLM V0](https://github.com/operator-framework/operator-lifecycle-manager). The user experience captured in the OLM V1 PRD introduces many requirements that are best satisfied by a microservices architecture. The OLM V1 experience currently relies on two projects: + - [The Operator-Controller project](https://github.com/operator-framework/operator-controller/), which is the top level component allowing users to specify operators they'd like to install. - [The Catalogd project](https://github.com/operator-framework/catalogd/), which hosts operator content and helps users discover installable content. @@ -45,6 +46,7 @@ Please keep this workflow in mind as you read through the document. ## How are Milestones Designed? It's unreasonable to attempt to consider all of the design requirements laid out in the [OLM V1 PRD](https://docs.google.com/document/d/1-vsZ2dAODNfoHb7Nf0fbYeKDF7DUqEzS9HqgeMCvbDs/edit) from the onset of the project. Instead, the community attempts to design Milestones with the following principles: + - Milestones are tightly scoped units of work, ideally lasting one to three weeks. - Milestones are derived from the OLM V1 PRD. - Milestones are "demo driven", meaning that a set of acceptance criteria is defined upfront and the milestone is done as soon as some member of the community can run the demo. @@ -52,7 +54,7 @@ It's unreasonable to attempt to consider all of the design requirements laid out This "demo driven" development model will allow us to collect user experience and regularly course correct based on user feedback. Subsequent milestone may revert features or change the user experience based on community feedback. -The project maintainer will create a [GitHub Discussion](github.com/operator-framework/operator-controller/discussions) for the upcoming milestone once we've finalized the current milestone. Please feel encouraged to contribute suggestions for the milestone in the discussion. +The project maintainer will create a [GitHub Discussion](https://github.com/operator-framework/operator-controller/discussions) for the upcoming milestone once we've finalized the current milestone. Please feel encouraged to contribute suggestions for the milestone in the discussion. ## Where are Operator Controller Milestones? @@ -67,6 +69,7 @@ As discussed earlier, the operator-controller adheres to a microservice architec ## Submitting Issues Unsure where to submit an issue? + - [The Operator-Controller project](https://github.com/operator-framework/operator-controller/), which is the top level component allowing users to specify operators they'd like to install. - [The Catalogd project](https://github.com/operator-framework/catalogd/), which hosts operator content and helps users discover installable content. @@ -87,7 +90,7 @@ approach of changes. When contributing changes that require a new dependency, check whether it's feasible to directly vendor that code [without introducing a new dependency](https://go-proverbs.github.io/). -Currently, PRs require at least one approval from a operator-controller maintainer in order to get merged. +Currently, PRs require at least one approval from an operator-controller maintainer in order to get merged. ### Code style diff --git a/Makefile b/Makefile index 746fdfb6b0..49a707b3c3 100644 --- a/Makefile +++ b/Makefile @@ -312,17 +312,18 @@ quickstart: $(KUSTOMIZE) manifests #EXHELP Generate the installation release man OPERATOR_CONTROLLER_API_REFERENCE_FILENAME := operator-controller-api-reference.md CATALOGD_API_REFERENCE_FILENAME := catalogd-api-reference.md CATALOGD_TMP_DIR := $(ROOT_DIR)/.catalogd-tmp/ +API_REFERENCE_DIR := $(ROOT_DIR)/docs/api-reference crd-ref-docs: $(CRD_REF_DOCS) #EXHELP Generate the API Reference Documents. - rm -f $(ROOT_DIR)/docs/refs/api/$(OPERATOR_CONTROLLER_API_REFERENCE_FILENAME) + rm -f $(API_REFERENCE_DIR)/$(OPERATOR_CONTROLLER_API_REFERENCE_FILENAME) $(CRD_REF_DOCS) --source-path=$(ROOT_DIR)/api \ - --config=$(ROOT_DIR)/docs/refs/api/crd-ref-docs-gen-config.yaml \ - --renderer=markdown --output-path=$(ROOT_DIR)/docs/refs/api/$(OPERATOR_CONTROLLER_API_REFERENCE_FILENAME); + --config=$(API_REFERENCE_DIR)/crd-ref-docs-gen-config.yaml \ + --renderer=markdown --output-path=$(API_REFERENCE_DIR)/$(OPERATOR_CONTROLLER_API_REFERENCE_FILENAME); rm -rf $(CATALOGD_TMP_DIR) git clone --depth 1 --branch $(CATALOGD_VERSION) https://github.com/operator-framework/catalogd $(CATALOGD_TMP_DIR) - rm -f $(ROOT_DIR)/docs/refs/api/$(CATALOGD_API_REFERENCE_FILENAME) + rm -f $(API_REFERENCE_DIR)/$(CATALOGD_API_REFERENCE_FILENAME) $(CRD_REF_DOCS) --source-path=$(CATALOGD_TMP_DIR)/api \ - --config=$(ROOT_DIR)/docs/refs/api/crd-ref-docs-gen-config.yaml \ - --renderer=markdown --output-path=$(ROOT_DIR)/docs/refs/api/$(CATALOGD_API_REFERENCE_FILENAME) + --config=$(API_REFERENCE_DIR)/crd-ref-docs-gen-config.yaml \ + --renderer=markdown --output-path=$(API_REFERENCE_DIR)/$(CATALOGD_API_REFERENCE_FILENAME) rm -rf $(CATALOGD_TMP_DIR)/ VENVDIR := $(abspath docs/.venv) diff --git a/README.md b/README.md index c4a1ce3afb..298d7098d1 100644 --- a/README.md +++ b/README.md @@ -2,137 +2,25 @@ The operator-controller is the central component of Operator Lifecycle Manager (OLM) v1. It extends Kubernetes with an API through which users can install extensions. -## Mission +## Overview + +OLM v1 is the follow-up to [OLM v0](https://github.com/operator-framework/operator-lifecycle-manager). Its purpose is to provide APIs, +controllers, and tooling that support the packaging, distribution, and lifecycling of Kubernetes extensions. It aims to: -OLM’s purpose is to provide APIs, controllers, and tooling that support the packaging, distribution, and lifecycling of Kubernetes extensions. It aims to: - align with Kubernetes designs and user assumptions - provide secure, high-quality, and predictable user experiences centered around declarative GitOps concepts - give cluster admins the minimal necessary controls to build their desired cluster architectures and to have ultimate control -## Overview - -OLM v1 is the follow-up to OLM v0, located [here](https://github.com/operator-framework/operator-lifecycle-manager). - OLM v1 consists of two different components: + * operator-controller (this repository) * [catalogd](https://github.com/operator-framework/catalogd) -For a more complete overview of OLM v1 and how it differs from OLM v0, see our [overview](docs/olmv1_overview.md). - -### Installation - -The following script will install OLMv1 on a Kubernetes cluster. If you don't have one, you can deploy a Kubernetes cluster with [KIND](https://sigs.k8s.io/kind). - -> [!CAUTION] -> Operator-Controller depends on [cert-manager](https://cert-manager.io/). Running the following command -> may affect an existing installation of cert-manager and cause cluster instability. - -The latest version of Operator Controller can be installed with the following command: - -```bash -curl -L -s https://github.com/operator-framework/operator-controller/releases/latest/download/install.sh | bash -s -``` - -## Getting Started with OLM v1 - -This quickstart procedure will guide you through the following processes: -* Deploying a catalog -* Installing, upgrading, or downgrading an extension -* Deleting catalogs and extensions - -### Create a Catalog - -OLM v1 is designed to source content from an on-cluster catalog in the file-based catalog ([FBC](https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs)) format. -These catalogs are deployed and configured through the `ClusterCatalog` resource. More information on adding catalogs -can be found [here](./docs/Tasks/adding-a-catalog). - -The following example uses the official [OperatorHub](https://operatorhub.io) catalog that contains many different -extensions to choose from. Note that this catalog contains packages designed to work with OLM v0, and that not all packages -will work with OLM v1. More information on catalog exploration and content compatibility can be found [here](./docs/refs/catalog-queries.md). - -To create the catalog, run the following command: - -```bash -# Create ClusterCatalog -kubectl apply -f - < + + + +logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/drafts/controlling-catalog-selection.md b/docs/concepts/controlling-catalog-selection.md similarity index 94% rename from docs/drafts/controlling-catalog-selection.md rename to docs/concepts/controlling-catalog-selection.md index e91a1eb0f1..544f36be51 100644 --- a/docs/drafts/controlling-catalog-selection.md +++ b/docs/concepts/controlling-catalog-selection.md @@ -27,7 +27,7 @@ spec: catalog: selector: matchLabels: - olm.operatorframework.io/metadata.name: my-catalog + olm.operatorframework.io/metadata.name: my-content-management ``` In this example, only the catalog named `my-catalog` will be considered when resolving `my-package`. @@ -93,7 +93,7 @@ spec: - key: olm.operatorframework.io/metadata.name operator: NotIn values: - - unwanted-catalog + - unwanted-content-management ``` This excludes the catalog named `unwanted-catalog` from consideration. @@ -134,7 +134,7 @@ spec: source: type: image image: - ref: quay.io/example/high-priority-catalog:latest + ref: quay.io/example/high-priority-content-management:latest ``` Catalogs have a default priority of `0`. The priority can be any 32-bit integer. Catalogs with higher priority values are preferred during bundle resolution. @@ -171,7 +171,7 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera source: type: image image: - ref: quay.io/example/catalog-a:latest + ref: quay.io/example/content-management-a:latest ``` ```yaml @@ -186,7 +186,7 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera source: type: image image: - ref: quay.io/example/catalog-b:latest + ref: quay.io/example/content-management-b:latest ``` NB: an `olm.operatorframework.io/metadata.name` label will be added automatically to ClusterCatalogs when applied @@ -209,8 +209,8 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera 3. **Apply the Resources** ```shell - kubectl apply -f catalog-a.yaml - kubectl apply -f catalog-b.yaml + kubectl apply -f content-management-a.yaml + kubectl apply -f content-management-b.yaml kubectl apply -f install-my-operator.yaml ``` diff --git a/docs/refs/crd-upgrade-safety.md b/docs/concepts/crd-upgrade-safety.md similarity index 100% rename from docs/refs/crd-upgrade-safety.md rename to docs/concepts/crd-upgrade-safety.md diff --git a/docs/drafts/single-owner-objects.md b/docs/concepts/single-owner-objects.md similarity index 100% rename from docs/drafts/single-owner-objects.md rename to docs/concepts/single-owner-objects.md index 0ed7dfcac5..0553f70a8b 100644 --- a/docs/drafts/single-owner-objects.md +++ b/docs/concepts/single-owner-objects.md @@ -1,4 +1,3 @@ - # OLM Ownership Enforcement for `ClusterExtensions` In OLM, **a Kubernetes resource can only be owned by a single `ClusterExtension` at a time**. This ensures that resources within a Kubernetes cluster are managed consistently and prevents conflicts between multiple `ClusterExtensions` attempting to control the same resource. @@ -15,6 +14,7 @@ Operator bundles provide `CustomResourceDefinitions` (CRDs), which are part of a ### 2. `ClusterExtensions` Cannot Share Objects + OLM's single-owner policy means that **`ClusterExtensions` cannot share ownership of any resources**. If one `ClusterExtension` manages a specific resource (e.g., a `Deployment`, `CustomResourceDefinition`, or `Service`), another `ClusterExtension` cannot claim ownership of the same resource. Any attempt to do so will be blocked by the system. ## Error Messages diff --git a/docs/drafts/upgrade-support.md b/docs/concepts/upgrade-support.md similarity index 99% rename from docs/drafts/upgrade-support.md rename to docs/concepts/upgrade-support.md index 367a57ec1b..9bc6e31adf 100644 --- a/docs/drafts/upgrade-support.md +++ b/docs/concepts/upgrade-support.md @@ -1,3 +1,8 @@ +--- +hide: + - toc +--- + # Upgrade support This document explains how OLM v1 handles upgrades. diff --git a/docs/drafts/version-ranges.md b/docs/concepts/version-ranges.md similarity index 98% rename from docs/drafts/version-ranges.md rename to docs/concepts/version-ranges.md index d247cc19f5..75e88f04e6 100644 --- a/docs/drafts/version-ranges.md +++ b/docs/concepts/version-ranges.md @@ -4,7 +4,7 @@ This document explains how to specify a version range to install or update an ex You define a version range in a ClusterExtension's custom resource (CR) file. -## Specifying a version range in the CR +### Specifying a version range in the CR If you specify a version range in the ClusterExtension's CR, OLM 1.0 installs or updates the latest version of the extension that can be resolved within the version range. The resolved version is the latest version of the extension that satisfies the dependencies and constraints of the extension and the environment. diff --git a/docs/contribute/contributing.md b/docs/contribute/contributing.md new file mode 120000 index 0000000000..f939e75f21 --- /dev/null +++ b/docs/contribute/contributing.md @@ -0,0 +1 @@ +../../CONTRIBUTING.md \ No newline at end of file diff --git a/docs/drafts/developer.md b/docs/contribute/developer.md similarity index 98% rename from docs/drafts/developer.md rename to docs/contribute/developer.md index 31959de6c1..b97c9d693b 100644 --- a/docs/drafts/developer.md +++ b/docs/contribute/developer.md @@ -177,4 +177,4 @@ done ## Contributing -Refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for more information. +Refer to [CONTRIBUTING.md](contributing.md) for more information. diff --git a/docs/css/extra.css b/docs/css/extra.css new file mode 100644 index 0000000000..0553b3b972 --- /dev/null +++ b/docs/css/extra.css @@ -0,0 +1,10 @@ +/* Hide banner title */ +.md-header__title { + visibility: hidden; +} + +/* Make top-level navigation items bold */ +.md-nav__item--active > .md-nav__link, /* Active top-level items */ +.md-nav__item--nested > .md-nav__link { /* Nested top-level items */ + font-weight: bold; +} \ No newline at end of file diff --git a/docs/drafts/Tasks/create-installer-service-account.md b/docs/drafts/Tasks/create-installer-service-account.md deleted file mode 100644 index e66c06076e..0000000000 --- a/docs/drafts/Tasks/create-installer-service-account.md +++ /dev/null @@ -1,3 +0,0 @@ -# Create Installer Service Account - -Placeholder. We need to document this. \ No newline at end of file diff --git a/docs/drafts/permissions-for-owner-references-permission-enforcement-plugin.md b/docs/drafts/permissions-for-owner-references-permission-enforcement-plugin.md deleted file mode 100644 index f80d332e06..0000000000 --- a/docs/drafts/permissions-for-owner-references-permission-enforcement-plugin.md +++ /dev/null @@ -1,13 +0,0 @@ -# Configuring a service account when the cluster uses the `OwnerReferencesPermissionEnforcement` admission plugin - -The [`OwnerReferencesPermissionEnforcement`](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement) admission plugin requires a user to have permission to set finalizers on owner objects when creating or updating an object to contain an `ownerReference` with `blockOwnerDeletion: true`. - -When operator-controller installs or upgrades a `ClusterExtension`, it sets an `ownerReference` on each object with `blockOwnerDeletion: true`. Therefore serviceaccounts configured in `.spec.serviceAccount.name` must have the following permission in a bound `ClusterRole`: - - ```yaml - - apiGroups: ["olm.operatorframework.io"] - resources: ["clusterextensions/finalizers"] - verbs: ["update"] - resourceNames: [""] - ``` - diff --git a/docs/drafts/provided-serviceaccount.md b/docs/drafts/provided-serviceaccount.md deleted file mode 100644 index 33f4501e9b..0000000000 --- a/docs/drafts/provided-serviceaccount.md +++ /dev/null @@ -1,31 +0,0 @@ -# Provided ServiceAccount for ClusterExtension Installation and Management - -Adhering to OLM v1's "Secure by Default" tenet, OLM v1 does not have the permissions -necessary to install content. This follows the least privilege principle and reduces -the chance of a [confused deputy attack](https://en.wikipedia.org/wiki/Confused_deputy_problem). -Instead, users must explicitly specify a ServiceAccount that will be used to perform the -installation and management of a specific ClusterExtension. The ServiceAccount is specified -in the ClusterExtension manifest as follows: - -```yaml -apiVersion: olm.operatorframework.io/v1alpha1 -kind: ClusterExtension -metadata: - name: argocd -spec: - source: - sourceType: Catalog - catalog: - packageName: argocd-operator - version: 0.6.0 - install: - namespace: argocd - serviceAccount: - name: argocd-installer -``` - -The ServiceAccount must be configured with the RBAC permissions required by the ClusterExtension. -If the permissions do not meet the minimum requirements, installation will fail. If no ServiceAccount -is provided in the ClusterExtension manifest, then the manifest will be rejected. - -//TODO: Add link to documentation on determining least privileges required for the ServiceAccount \ No newline at end of file diff --git a/docs/drafts/refs/olmv1-limitations.md b/docs/drafts/refs/olmv1-limitations.md deleted file mode 100644 index 1c351f9e91..0000000000 --- a/docs/drafts/refs/olmv1-limitations.md +++ /dev/null @@ -1,3 +0,0 @@ -# Current OLM v1 Limitations - -Placeholder. We need to document this. \ No newline at end of file diff --git a/docs/drafts/support-watchNamespaces.md b/docs/drafts/support-watchNamespaces.md deleted file mode 100644 index b10c279cc3..0000000000 --- a/docs/drafts/support-watchNamespaces.md +++ /dev/null @@ -1,24 +0,0 @@ -# Install Modes and WatchNamespaces in OMLv1 - -Operator Lifecycle Manager (OLM) operates with cluster-admin privileges, enabling it to grant necessary permissions to the Extensions it deploys. For extensions packaged as [`RegistryV1`][registryv1] bundles, it's the responsibility of the authors to specify supported `InstallModes` in the ClusterServiceVersion ([CSV][csv]). InstallModes define the operational scope of the extension within the Kubernetes cluster, particularly in terms of namespace availability. The four recognized InstallModes are as follows: - -1. OwnNamespace: This mode allows the extension to monitor and respond to events within its own deployment namespace. -1. SingleNamespace: In this mode, the extension is set up to observe events in a single, specific namespace other than the one it is deployed in. -1. MultiNamespace: This enables the extension to function across multiple specified namespaces. -1. AllNamespaces: Under this mode, the extension is equipped to monitor events across all namespaces within the cluster. - -When creating a cluster extension, users have the option to define a list of `watchNamespaces`. This list determines the specific namespaces within which they intend the operator to operate. The configuration of `watchNamespaces` must align with the InstallModes supported by the extension as specified by the bundle author. The supported configurations in the order of preference are as follows: - - -| Length of `watchNamespaces` specified through ClusterExtension | Allowed values | Supported InstallMode in CSV | Description | -|------------------------------|-------------------------------------------------------|----------------------|-----------------------------------------------------------------| -| **0 (Empty/Unset)** | - | AllNamespaces | Extension monitors all namespaces. | -| | - | OwnNamespace | Supported when `AllNamespaces` is false. Extension only active in its deployment namespace. | -| **1 (Single Entry)** | `""` (Empty String) | AllNamespaces | Extension monitors all namespaces. | -| | Entry equals Install Namespace | OwnNamespace | Extension watches only its install namespace. | -| | Entry is a specific namespace (not the Install Namespace) | SingleNamespace | Extension monitors a single, specified namespace in the spec. | -| **>1 (Multiple Entries)** | Entries are specific, multiple namespaces | MultiNamespace | Extension monitors each of the specified multiple namespaces in the spec. - - -[registryv1]: https://olm.operatorframework.io/docs/tasks/creating-operator-manifests/#writing-your-operator-manifests -[csv]: https://olm.operatorframework.io/docs/concepts/crds/clusterserviceversion/ \ No newline at end of file diff --git a/docs/getting-started/olmv1_getting_started.md b/docs/getting-started/olmv1_getting_started.md new file mode 100644 index 0000000000..77760f4fc7 --- /dev/null +++ b/docs/getting-started/olmv1_getting_started.md @@ -0,0 +1,115 @@ +### Installation + +The following script will install OLMv1 on a Kubernetes cluster. If you don't have one, you can deploy a Kubernetes cluster with [KIND](https://sigs.k8s.io/kind). + +> [!CAUTION] +> Operator-Controller depends on [cert-manager](https://cert-manager.io/). Running the following command +> may affect an existing installation of cert-manager and cause cluster instability. + +The latest version of Operator Controller can be installed with the following command: + +```bash +curl -L -s https://github.com/operator-framework/operator-controller/releases/latest/download/install.sh | bash -s +``` + +### Getting Started with OLM v1 + +This quickstart procedure will guide you through the following processes: + +* Deploying a catalog +* Installing, upgrading, or downgrading an extension +* Deleting catalogs and extensions + +### Create a Catalog + +OLM v1 is designed to source content from an on-cluster catalog in the file-based catalog ([FBC](https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs)) format. +These catalogs are deployed and configured through the `ClusterCatalog` resource. More information on adding catalogs +can be found [here](../tutorials/add-catalog.md). + +The following example uses the official [OperatorHub](https://operatorhub.io) catalog that contains many different +extensions to choose from. Note that this catalog contains packages designed to work with OLM v0, and that not all packages +will work with OLM v1. More information on catalog exploration and content compatibility can be found [here](../howto/catalog-queries.md). + +To create the catalog, run the following command: + +```bash +# Create ClusterCatalog +kubectl apply -f - < ``` - - ## Package queries Available packages in a catalog diff --git a/docs/drafts/derive-serviceaccount.md b/docs/howto/derive-service-account.md similarity index 96% rename from docs/drafts/derive-serviceaccount.md rename to docs/howto/derive-service-account.md index fec1649df7..599fc103a8 100644 --- a/docs/drafts/derive-serviceaccount.md +++ b/docs/howto/derive-service-account.md @@ -1,7 +1,7 @@ # Derive minimal ServiceAccount required for ClusterExtension Installation and Management -OLM v1 does not have permission to install extensions on a cluster by default. In order to install a [supported bundle](../refs/supported-extensions.md), -OLM must be provided a ServiceAccount configured with the appropriate permissions. For more information, see the [provided ServiceAccount](./provided-serviceaccount.md) documentation. +OLM v1 does not have permission to install extensions on a cluster by default. In order to install a [supported bundle](../project/olmv1_limitations.md), +OLM must be provided a ServiceAccount configured with the appropriate permissions. This document serves as a guide for how to derive the RBAC necessary to install a bundle. @@ -12,6 +12,7 @@ This bundle image contains all the manifests that make up the extension (e.g. `C as well as a [`ClusterServiceVersion`](https://olm.operatorframework.io/docs/concepts/crds/clusterserviceversion/) (CSV) that describes the extension and its service account's permission requirements. The service account must have permissions to: + - create and manage the extension's `CustomResourceDefinition`s - create and manage the resources packaged in the bundle - grant the extension controller's service account the permissions it requires for its operation @@ -30,7 +31,7 @@ Depending on the scope, each permission will need to be added to either a `Clust ### Example The following example illustrates the process of deriving the minimal RBAC required to install the [ArgoCD Operator](https://operatorhub.io/operator/argocd-operator) [v0.6.0](https://operatorhub.io/operator/argocd-operator/alpha/argocd-operator.v0.6.0) provided by [OperatorHub.io](https://operatorhub.io/). -The final permission set can be found in the [ClusterExtension sample manifest](../../config/samples/olm_v1alpha1_clusterextension.yaml) in the [samples](../../config/samples/olm_v1alpha1_clusterextension.yaml) directory. +The final permission set can be found in the [ClusterExtension sample manifest](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1alpha1_clusterextension.yaml) in the [samples](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1alpha1_clusterextension.yaml) directory. The bundle includes the following manifests, which can be found [here](https://github.com/argoproj-labs/argocd-operator/tree/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0): @@ -301,7 +302,7 @@ Once the installer service account required cluster-scoped and namespace-scoped 6. Create the `RoleBinding` between the installer service account and its role 7. Create the `ClusterExtension` -A manifest with the full set of resources can be found [here](../../config/samples/olm_v1alpha1_clusterextension.yaml). +A manifest with the full set of resources can be found [here](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1alpha1_clusterextension.yaml). ### Alternatives @@ -346,6 +347,6 @@ kubectl create clusterrolebinding my-cluster-extension-installer-role-binding \ #### hack/tools/catalog In the spirit of making this process more tenable until the proper tools are in place, the scripts -in [hack/tools/catalogs](../../hack/tools/catalogs) were created to help the user navigate and search catalogs as well +in [hack/tools/catalogs](https://github.com/operator-framework/operator-controller/blob/main/hack/tools/catalogs) were created to help the user navigate and search catalogs as well as to generate the minimal RBAC requirements. These tools are offered as is, with no guarantees on their correctness, support, or maintenance. For more information, see [Hack Catalog Tools](https://github.com/operator-framework/operator-controller/blob/main/hack/tools/catalogs/README.md). \ No newline at end of file diff --git a/docs/drafts/how-to-channel-based-upgrades.md b/docs/howto/how-to-channel-based-upgrades.md similarity index 96% rename from docs/drafts/how-to-channel-based-upgrades.md rename to docs/howto/how-to-channel-based-upgrades.md index f1692422fd..501a7f9519 100644 --- a/docs/drafts/how-to-channel-based-upgrades.md +++ b/docs/howto/how-to-channel-based-upgrades.md @@ -1,4 +1,4 @@ -## How-to: Channel-Based Automatic Upgrades +# Channel-Based Automatic Upgrades A "channel" is a package author defined stream of updates for an extension. A set of channels can be set in the Catalog source to restrict automatic updates to the set of versions defined in those channels. diff --git a/docs/drafts/how-to-pin-version.md b/docs/howto/how-to-pin-version.md similarity index 87% rename from docs/drafts/how-to-pin-version.md rename to docs/howto/how-to-pin-version.md index 17bd7e1c61..606b994aad 100644 --- a/docs/drafts/how-to-pin-version.md +++ b/docs/howto/how-to-pin-version.md @@ -1,4 +1,4 @@ -## How-to: Version Pin and Disable Automatic Updates +# Pin Version and Disable Automatic Updates To disable automatic updates, and pin the version of an extension, set `version` in the Catalog source to a specific version (e.g. 1.2.3). @@ -21,4 +21,4 @@ spec: name: argocd-installer ``` -For more information on SemVer version ranges see [version ranges](version-ranges.md) +For more information on SemVer version ranges see [version ranges](../concepts/version-ranges.md) diff --git a/docs/drafts/how-to-version-range-upgrades.md b/docs/howto/how-to-version-range-upgrades.md similarity index 91% rename from docs/drafts/how-to-version-range-upgrades.md rename to docs/howto/how-to-version-range-upgrades.md index 9a5c305ee4..ddb753fbad 100644 --- a/docs/drafts/how-to-version-range-upgrades.md +++ b/docs/howto/how-to-version-range-upgrades.md @@ -1,4 +1,4 @@ -## How-to: Version Range Automatic Updates +# Version Range Automatic Updates Set the version for the desired package in the Catalog source to a comparison string, like `">=3.0, <3.6"`, to restrict the automatic updates to the version range. Any new version of the extension released in the catalog within this range will be automatically applied. @@ -21,4 +21,4 @@ spec: name: argocd-installer ``` -For more information on SemVer version ranges see [version-rages](version-ranges.md) \ No newline at end of file +For more information on SemVer version ranges see [version-rages](../concepts/version-ranges.md) \ No newline at end of file diff --git a/docs/drafts/how-to-z-stream-upgrades.md b/docs/howto/how-to-z-stream-upgrades.md similarity index 90% rename from docs/drafts/how-to-z-stream-upgrades.md rename to docs/howto/how-to-z-stream-upgrades.md index 835abc2b55..8666e09b74 100644 --- a/docs/drafts/how-to-z-stream-upgrades.md +++ b/docs/howto/how-to-z-stream-upgrades.md @@ -1,4 +1,4 @@ -## How-to: Z-Stream Automatic Updates +# Z-Stream Automatic Updates To restrict automatic updates to only z-stream patches and avoid breaking changes, use the `"~"` version range operator when setting the version for the desired package in Catalog source. @@ -21,4 +21,4 @@ spec: name: argocd-installer ``` -For more information on SemVer version ranges see [version ranges](version-ranges.md) +For more information on SemVer version ranges see [version ranges](../concepts/version-ranges.md) diff --git a/docs/index.md b/docs/index.md index 6fda98519f..942cdd938e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,24 @@ -# What is Operator Lifecycle Manager (OLM)? +--- +hide: + - toc +--- -Operator Lifecycle Manager (OLM) is an open-source [CNCF](https://www.cncf.io/) project with the mission to manage the -lifecycle of cluster extensions centrally and declaratively on Kubernetes clusters. Its purpose is to make installing, +# Overview + +Operator Lifecycle Manager (OLM) is an open-source [CNCF](https://www.cncf.io/) project with the mission to manage the +lifecycle of cluster extensions centrally and declaratively on Kubernetes clusters. Its purpose is to make installing, running, and updating functional extensions to the cluster easy, safe, and reproducible for cluster administrators and PaaS administrators. -Previously, OLM was focused on a particular type of cluster extension: [Operators](https://operatorhub.io/what-is-an-operator#:~:text=is%20an%20Operator-,What%20is%20an%20Operator%20after%20all%3F,or%20automation%20software%20like%20Ansible.). +Previously, OLM was focused on a particular type of cluster extension: [Operators](https://operatorhub.io/what-is-an-operator#:~:text=is%20an%20Operator-,What%20is%20an%20Operator%20after%20all%3F,or%20automation%20software%20like%20Ansible.). Operators are a method of packaging, deploying, and managing a Kubernetes application. An Operator is composed of one or more controllers paired with one or both of the following objects: -* One or more API extensions + +* One or more API extensions * One or more [CustomResourceDefinitions](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) (CRDs). OLM helped define lifecycles for these extensions: from packaging and distribution to installation, configuration, upgrade, and removal. The first iteration of OLM, termed OLM v0, included several concepts and features targeting the stability, security, and supportability of the life-cycled applications, for instance: + * A dependency model that enabled cluster extensions to focus on their primary purpose by delegating out of scope behavior to dependencies * A constraint model that allowed cluster extension developers to define support limitations such as conflicting extensions, and minimum kubernetes versions * A namespace-based multi-tenancy model in lieu of namespace-scoped CRDs @@ -20,11 +27,13 @@ The first iteration of OLM, termed OLM v0, included several concepts and feature Since its initial release, OLM has helped catalyse the growth of Operators throughout the Kubernetes ecosystem. [OperatorHub.io](https://operatorhub.io/) is a popular destination for discovering Operators, and boasts over 300 packages from many different vendors. -# Why are we building OLM v1? +## Why are we building OLM v1? + +The Operator Lifecycle Manager (OLM) has been in production for over five years, serving as a critical component in managing Kubernetes Operators. +Over this time, the community has gathered valuable insights from real-world usage, identifying both the strengths and limitations of the initial design, +and validating the design's initial assumptions. This process led to a complete redesign and rewrite of OLM that, compared to its predecessor, aims to +provide: -OLM v0 has been in production for over 5 years, and the community to leverage this experience and question the initial -goals and assumptions of the project. OLM v1 is a complete redesign and rewrite of OLM taking into account this accumulated experience. -Compared to its predecessor, amongst other things, OLM v1 aims to provide: * A simpler API surface and mental model * Less opinionated automation and greater flexibility * Support for Kubernetes applications beyond only Operators @@ -32,18 +41,5 @@ Compared to its predecessor, amongst other things, OLM v1 aims to provide: * Helm Chart support * GitOps support -For an in-depth look at OLM v1, please see the [OLM v1 Overview](olmv1_overview.md) and the [Roadmap](olmv1_roadmap.md). - -# The OLM community - -In this next iteration of OLM, the community has also taken care to make it as contributor-friendly as possible, and welcomes new contributors. -The project is tracked in a [GitHub project](https://github.com/orgs/operator-framework/projects/8/), -which provides a great entry point to quickly find something interesting to work on and contribute. - -You can reach out to the OLM community for feedbacks/discussions/contributions in the following channels: - - * Kubernetes Slack channel: [#olm-dev](https://kubernetes.slack.com/messages/olm-dev) - * [Operator Framework on Google Groups](https://groups.google.com/forum/#!forum/operator-framework) - * Weekly in-person Working Group meeting: [olm-wg](https://github.com/operator-framework/community#operator-lifecycle-manager-working-group) - -For further information on contributing, please consult the [Contribution Guide](../CONTRIBUTING.md) +To learn more about where v1 one came from, and where it's going, please see [Multi-Tenancy Challenges, Lessons Learned, and Design Shifts](project/olmv1_design_decisions.md) +and our feature [Roadmap](project/olmv1_roadmap.md). diff --git a/docs/drafts/architecture.md b/docs/project/olmv1_architecture.md similarity index 98% rename from docs/drafts/architecture.md rename to docs/project/olmv1_architecture.md index 5be36f9af8..1672fae648 100644 --- a/docs/drafts/architecture.md +++ b/docs/project/olmv1_architecture.md @@ -1,5 +1,9 @@ +--- +hide: + - toc +--- -## OLM V1 Architecture +# OLM V1 Architecture This document describes the OLM v1 architecture. OLM v1 consists of two main components: @@ -54,6 +58,7 @@ flowchart TB ### Operator-controller: operator-controller is the central component of OLM v1. It is responsible: + * managing a cache of catalog metadata provided by catalogd through its HTTP server * keeping the catalog metadata cache up-to-date with the current state of the catalogs * locating the right `registry+v1` bundle, if any, that meet the constraints expressed in the `ClusterExtension` resource, such as package name, version range, channel, etc. given the current state of the cluster @@ -61,6 +66,7 @@ operator-controller is the central component of OLM v1. It is responsible: * applying the bundle manifests: installing or updating the content. It has three main sub-components: + * Cluster Extension Controller: * Queries the catalogd (catalogd HTTP Server) to get catalog information. * Once received the catalog information is saved to catalog-cache. The cache will be updated automatically if a Catalog is noticed to have a different resolved image reference. diff --git a/docs/project/olmv1_community.md b/docs/project/olmv1_community.md new file mode 100644 index 0000000000..55e8cf1b85 --- /dev/null +++ b/docs/project/olmv1_community.md @@ -0,0 +1,15 @@ + +OLM is an open-source [CNCF](https://www.cncf.io/) project with a friendly and supportive community of developers, testers, + and documentation experts with a passion for Kubernetes. + +Through the effort of redesigning OLM, the community also took the opportunity to make the project more accessible, +and contributor-friendly through its weekly meetings, continuous planning, and a [GitHub project](https://github.com/orgs/operator-framework/projects/8/) + tracker that provides a convenient entry point to quickly find something interesting to work on and contribute. + +You can reach out to the OLM community for feedbacks/discussions/contributions in the following channels: + +* Kubernetes Slack channel: [#olm-dev](https://kubernetes.slack.com/messages/olm-dev) +* [Operator Framework on Google Groups](https://groups.google.com/forum/#!forum/operator-framework) +* Weekly in-person Working Group meeting: [olm-wg](https://github.com/operator-framework/community#operator-lifecycle-manager-working-group) + +For further information on contributing, please consult the [Contribution Guide](../contribute/contributing.md) \ No newline at end of file diff --git a/docs/olmv1_overview.md b/docs/project/olmv1_design_decisions.md similarity index 71% rename from docs/olmv1_overview.md rename to docs/project/olmv1_design_decisions.md index 417f6d5baf..f8017455d9 100644 --- a/docs/olmv1_overview.md +++ b/docs/project/olmv1_design_decisions.md @@ -1,48 +1,56 @@ -# OLM v1 Overview +# Multi-Tenancy Challenges, Lessons Learned, and Design Shifts -## What won't OLMv1 do that OLMv0 did? +This provides historical context on the design explorations and challenges that led to substantial design shifts between +OLM v1 and its predecessor. It explains the technical reasons why OLM v1 cannot support major v0 features, such as, +multi-tenancy, and namespace-specific controller configurations. Finally, it highlights OLM v1’s shift toward +more secure, predictable, and simple operations while moving away from some of the complex, error-prone features of OLM v0. -TL;DR: OLMv1 cannot feasibly support multi-tenancy or any feature that assumes multi-tenancy. All multi-tenancy features end up falling over because of the global API system of Kubernetes. While this short conclusion may be unsatisfying, the reasons are complex and intertwined. +## What won't OLM v1 do that OLM v0 did? + +TL;DR: OLM v1 cannot feasibly support multi-tenancy or any feature that assumes multi-tenancy. All multi-tenancy features end up falling over because of the global API system of Kubernetes. While this short conclusion may be unsatisfying, the reasons are complex and intertwined. ### Historical Context Nearly every active contributor in the Operator Framework project contributed to design explorations and prototypes over an entire year. For each of these design explorations, there are complex webs of features and assumptions that are necessary to understand the context that ultimately led to a conclusion of infeasibility. Here is a sampling of some of the ideas we explored: -- [[WIP] OLM v1's approach to multi-tenancy](https://docs.google.com/document/d/1xTu7XadmqD61imJisjnP9A6k38_fiZQ8ThvZSDYszog/edit#heading=h.m19itc78n5rw) -- [OLMv1 Multi-tenancy Brainstorming](https://docs.google.com/document/d/1ihFuJR9YS_GWW4_p3qjXu3WjvK0NIPIkt0qGixirQO8/edit#heading=h.vy9860qq1j01) + +- [OLM v1's approach to multi-tenancy](https://docs.google.com/document/d/1xTu7XadmqD61imJisjnP9A6k38_fiZQ8ThvZSDYszog/edit#heading=h.m19itc78n5rw) +- [OLM v1 Multi-tenancy Brainstorming](https://docs.google.com/document/d/1ihFuJR9YS_GWW4_p3qjXu3WjvK0NIPIkt0qGixirQO8/edit#heading=h.vy9860qq1j01) ### Watched namespaces cannot be configured in a first-class API -OLMv1 will not have a first-class API for configuring the namespaces that a controller will watch. +OLM v1 will not have a first-class API for configuring the namespaces that a controller will watch. Kubernetes APIs are global. Kubernetes is designed with the assumption that a controller WILL reconcile an object no matter where it is in the cluster. However, Kubernetes does not assume that a controller will be successful when it reconciles an object. The Kubernetes design assumptions are: + - CRDs and their controllers are trusted cluster extensions. - If an object for an API exists a controller WILL reconcile it, no matter where it is in the cluster. -OLMv1 will make the same assumption that Kubernetes does and that users of Kubernetes APIs do. That is: If a user has RBAC to create an object in the cluster, they can expect that a controller exists that will reconcile that object. If this assumption does not hold, it will be considered a configuration issue, not an OLMv1 bug. +OLM v1 will make the same assumption that Kubernetes does and that users of Kubernetes APIs do. That is: If a user has RBAC to create an object in the cluster, they can expect that a controller exists that will reconcile that object. If this assumption does not hold, it will be considered a configuration issue, not an OLM v1 bug. This means that it is a best practice to implement and configure controllers to have cluster-wide permission to read and update the status of their primary APIs. It does not mean that a controller needs cluster-wide access to read/write secondary APIs. If a controller can update the status of its primary APIs, it can tell users when it lacks permission to act on secondary APIs. ### Dependencies based on watched namespaces -Since there will be no first-class support for configuration of watched namespaces, OLMv1 cannot resolve dependencies among bundles based on where controllers are watching. +Since there will be no first-class support for configuration of watched namespaces, OLM v1 cannot resolve dependencies among bundles based on where controllers are watching. -However, not all bundle constraints are based on dependencies among bundles from different packages. OLMv1 will be able to support constraints against cluster state. For example, OLMv1 could support a “kubernetesVersionRange” constraint that blocks installation of a bundle if the current kubernetes cluster version does not fall into the specified range. +However, not all bundle constraints are based on dependencies among bundles from different packages. OLM v1 will be able to support constraints against cluster state. For example, OLM v1 could support a “kubernetesVersionRange” constraint that blocks installation of a bundle if the current kubernetes cluster version does not fall into the specified range. #### Background -For packages that specify API-based dependencies, OLMv0’s dependency checker knows which controllers are watching which namespaces. While OLMv1 will have awareness of which APIs are present on a cluster (via the discovery API), it will not know which namespaces are being watched for reconciliation of those APIs. Therefore dependency resolution based solely on API availability would only work in cases where controllers are configured to watch all namespaces. +For packages that specify API-based dependencies, OLMv0’s dependency checker knows which controllers are watching which namespaces. While OLM v1 will have awareness of which APIs are present on a cluster (via the discovery API), it will not know which namespaces are being watched for reconciliation of those APIs. Therefore dependency resolution based solely on API availability would only work in cases where controllers are configured to watch all namespaces. For packages that specify package-based dependencies, OLMv0’s dependency checker again knows which controllers are watching which namespaces. This case is challenging for a variety of reasons: + 1. How would a dependency resolver know which extensions were installed (let alone which extensions were watching which namespaces)? If a user is running the resolver, they would be blind to an installed extension that is watching their namespace if they don’t have permission to list extensions in the installation namespace. If a controller is running the resolver, then it might leak information to a user about installed extensions that the user is not otherwise entitled to know. 2. Even if (1) could be overcome, the lack of awareness of watched namespaces means that the resolver would have to make assumptions. If only one controller is installed, is it watching the right set of namespaces to meet the constraint? If multiple controllers are installed, are any of them watching the right set of namespaces? Without knowing the watched namespaces of the parent and child controllers, a correct dependency resolver implementation is not possible to implement. -Note that regardless of the ability of OLMv1 to perform dependency resolution (now or in the future), OLMv1 will not automatically install a missing dependency when a user requests an operator. The primary reasoning is that OLMv1 will err on the side of predictability and cluster-administrator awareness. +Note that regardless of the ability of OLM v1 to perform dependency resolution (now or in the future), OLM v1 will not automatically install a missing dependency when a user requests an operator. The primary reasoning is that OLM v1 will err on the side of predictability and cluster-administrator awareness. ### "Watch namespace"-aware operator discoverability @@ -50,7 +58,7 @@ When operators add APIs to a cluster, these APIs are globally visible. As stated Therefore, the API discoverability story boils down to answering this question for the user: “What APIs do I have access to in a given namespace?” Fortunately, built-in APIs exist to answer this question: Kubernetes Discovery, SelfSubjectRulesReview (SSRR), and SelfSubjectAccessReview (SSAR). -However, helping users discover which actual controllers will reconcile those APIs is not possible unless OLMv1 knows which namespaces those controllers are watching. +However, helping users discover which actual controllers will reconcile those APIs is not possible unless OLM v1 knows which namespaces those controllers are watching. Any solution here would be unaware of where a controller is actually watching and could only know “is there a controller installed that provides an implementation of this API?”. However even knowledge of a controller installation is not certain. Any user can use the discovery, SSRR, and SSAR. Not all users can list all Extensions (see [User discovery of “available” APIs](#user-discovery-of-available-apis)). @@ -59,19 +67,21 @@ Any solution here would be unaware of where a controller is actually watching an The multi-tenancy promises that OLMv0 made were false promises. Kubernetes is not multi-tenant with respect to management of APIs (because APIs are global). Any promise that OLMv0 has around multi-tenancy evaporates when true tenant isolation attempts are made, and any attempt to fix a broken promise is actually just a bandaid on an already broken assumption. So where do we go from here? There are multiple solutions that do not involve OLM implementing full multi-tenancy support, some or all of which can be explored. + 1. Customers transition to a control plane per tenant 2. Extension authors update their operators to support customers’ multi-tenancy use cases 3. Extension authors with “simple” lifecycling concerns transition to other packaging and deployment strategies (e.g. helm charts) ### Single-tenant control planes -One choice for customers would be to adopt low-overhead single-tenant control planes in which every tenant can have full control over their APIs and controllers and be truly isolated (at the control plane layer at least) from other tenants. With this option, the things OLMv1 cannot do (listed above) are irrelevant, because the purpose of all of those features is to support multi-tenant control planes in OLM. +One choice for customers would be to adopt low-overhead single-tenant control planes in which every tenant can have full control over their APIs and controllers and be truly isolated (at the control plane layer at least) from other tenants. With this option, the things OLM v1 cannot do (listed above) are irrelevant, because the purpose of all of those features is to support multi-tenant control planes in OLM. The [Kubernetes multi-tenancy docs](https://kubernetes.io/docs/concepts/security/multi-tenancy/#virtual-control-plane-per-tenant) contain a good overview of the options in this space. Kubernetes vendors may also have their own virtual control plane implementations. ### Shift multi-tenant responsibility to operators There is a set of operators that both (a) provide fully namespace-scoped workload-style operands and that (b) provide a large amount of value to their users for advanced features like backup and migration. For these operators, the Operator Framework program would suggest that they shift toward supporting multi-tenancy directly. That would involve: + 1. Taking extreme care to avoid API breaking changes. 2. Supporting multiple versions of their operands in a single version of the operator (if required by users in multi-tenant clusters). 3. Maintaining support for versioned operands for the same period of time that the operator is supported for a given cluster version. @@ -79,7 +89,7 @@ There is a set of operators that both (a) provide fully namespace-scoped workloa ### Operator authors ship controllers outside of OLM -Some projects have been successful delivering and supporting their operator on Kubernetes, but outside of OLM, for example with helm-packaged operators. On this path, individual layered project teams have more flexibility in solving lifecycling problems for their users because they are unencumbered by OLM’s opinions. However the tradeoff is that those project teams and their users take on responsibility and accountability for safe upgrades, automation, and multi-tenant architectures. With OLMv1 no longer attempting to support multi-tenancy in a first-class way, these tradeoffs change and project teams may decide that a different approach is necessary. +Some projects have been successful delivering and supporting their operator on Kubernetes, but outside of OLM, for example with helm-packaged operators. On this path, individual layered project teams have more flexibility in solving lifecycling problems for their users because they are unencumbered by OLM’s opinions. However the tradeoff is that those project teams and their users take on responsibility and accountability for safe upgrades, automation, and multi-tenant architectures. With OLM v1 no longer attempting to support multi-tenancy in a first-class way, these tradeoffs change and project teams may decide that a different approach is necessary. This path does not necessarily mean a scattering of content in various places. It would still be possible to provide customers with a marketplace of content (e.g. see https://artifacthub.io/). @@ -110,10 +120,11 @@ OLM constantly monitors the state of all on-cluster resources for all the operat ### CRD Upgrade Safety Checks Before OLM upgrades a CRD, OLM performs a set of safety checks to identify any changes that potentially would have negative impacts, such as: + - data loss - incompatible schema changes -These checks may not be a guarantee that an upgrade is safe; instead, they are intended to provide an early warning sign for identifiable incompatibilities. False positives (OLMv1 claims a breaking change when there is none) and false negatives (a breaking change makes it through the check without being caught) are possible, at least while the OLMv1 team iterates on this feature. +These checks may not be a guarantee that an upgrade is safe; instead, they are intended to provide an early warning sign for identifiable incompatibilities. False positives (OLM v1 claims a breaking change when there is none) and false negatives (a breaking change makes it through the check without being caught) are possible, at least while the OLM v1 team iterates on this feature. ### User permissions management @@ -124,61 +135,65 @@ Also note that user permission management does not unlock operator discoverabili ### User discovery of “available” APIs In the future, the Operator Framework team could explore building an API similar to SelfSubjectAccessReview and SelfSubjectRulesReview that answers the question: -“What is the public metadata of all of the extensions that are installed on the cluster that provide APIs that I have permission for in namespace X?” +“What is the public metadata of all extensions that are installed on the cluster that provide APIs that I have permission for in namespace X?” One solution would be to join “installed extensions with user permissions”. If an installed extension provides an API that a user has RBAC permission for, that extension would be considered available to that user in that scope. This solution would not be foolproof: it makes the (reasonable) assumption that an administrator only configures RBAC for a user in a namespace where a controller is reconciling that object. If an administrator gives a user RBAC access to an API without also configuring that controller to watch the namespace that they have access to, the discovery solution would report an available extension, but then nothing would actually reconcile the object they create. This solution would tell users about API-only and API+controller bundles that are installed. It would not tell users about controller-only bundles, because they do not include APIs. -Other similar API-centric solutions could be explored as well. For example, pursuing enhancements to OLMv1 or core Kubernetes related to API metadata and/or grouping. +Other similar API-centric solutions could be explored as well. For example, pursuing enhancements to OLM v1 or core Kubernetes related to API metadata and/or grouping. -A key note here is that controller-specific metadata like the version of the controller that will reconcile the object in a certain namespace is not necessary for discovery. Discovery is primarily about driving user flows around presenting information and example usage of a group of APIs such that CLIs and UIs can provide rich experiences around interactions with available APIs. +A key insight here is that controller-specific metadata like the version of the controller that will reconcile the object in a certain namespace is not necessary for discovery. Discovery is primarily about driving user flows around presenting information and example usage of a group of APIs such that CLIs and UIs can provide rich experiences around interactions with available APIs. ## Approach -We will adhere to the following tenets in our approach for the design and implementation of OLMv1 +We will adhere to the following tenets in our approach for the design and implementation of OLM v1 ### Do not fight Kubernetes One of the key features of cloud-native applications/extensions/operators is that they typically come with a Kubernetes-based API (e.g. CRD) and a controller that reconciles instances of that API. In Kubernetes, API registration is cluster-scoped. It is not possible to register different APIs in different namespaces. Instances of an API can be cluster- or namespace-scoped. All APIs are global (they can be invoked/accessed regardless of namespace). For cluster-scoped APIs, the names of their instances must be unique. For example, it’s possible to have Nodes named “one” and “two”, but it’s not possible to have multiple Nodes named “two”. For namespace-scoped APIs, the names of their instances must be unique per namespace. The following illustrates this for ConfigMaps, a namespace-scoped API: Allowed + - Namespace: test, name: my-configmap - Namespace: other, name: my-configmap Disallowed + - Namespace: test, name: my-configmap - Namespace: test, name: my-configmap In cases where OLMv0 decides that joint ownership of CRDs will not impact different tenants, OLMv0 allows multiple installations of bundles that include the same named CRD, and OLMv0 itself manages the CRD lifecycle. This has security implications because it requires OLMv0 to act as a deputy, but it also pits OLM against the limitations of the Kubernetes API. OLMv0 promises that different versions of an operator can be installed in the cluster for use by different tenants without tenants being affected by each other. This is not a promise OLM can make because it is not possible to have multiple versions of the same CRD present on a cluster for different tenants. -In OLMv1, we will not design the core APIs and controllers around this promise. Instead, we will build an API where ownership of installed objects is not shared. Managed objects are owned by exactly one extension. +In OLM v1, we will not design the core APIs and controllers around this promise. Instead, we will build an API where ownership of installed objects is not shared. Managed objects are owned by exactly one extension. This pattern is generic, aligns with the Kubernetes API, and makes multi-tenancy a possibility, but not a guarantee or core concept. We will explore the implications of this design on existing OLMv0 registry+v1 bundles as part of the larger v0 to v1 migration design. For net new content, operator authors that intend multiple installations of operator on the same cluster would need to package their components to account for this ownership rule. Generally, this would entail separation along these lines: - CRDs, conversion webhook workloads, and admission webhook configurations and workloads, APIServices and workloads. - Controller workloads, service accounts, RBAC, etc. -OLMv1 will include primitives (e.g. templating) to make it possible to have multiple non-conflicting installations of bundles. +OLM v1 will include primitives (e.g. templating) to make it possible to have multiple non-conflicting installations of bundles. -However it should be noted that the purpose of these primitives is not to enable multi-tenancy. It is to enable administrators to provide configuration for the installation of an extension. The fact that operators can be packaged as separate bundles and parameterized in a way that permits multiple controller installations is incidental, and not something that OLMv1 will encourage or promote. +However, it should be noted that the purpose of these primitives is not to enable multi-tenancy. It is to enable administrators to provide configuration for the installation of an extension. The fact that operators can be packaged as separate bundles and parameterized in a way that permits multiple controller installations is incidental, and not something that OLM v1 will encourage or promote. ### Make OLM secure by default OLMv0 runs as cluster-admin, which is a security concern. OLMv0 has optional security controls for operator installations via the OperatorGroup, which allows a user with permission to create or update them to also set a ServiceAccount that will be used for authorization purposes on operator installations and upgrades in that namespace. If a ServiceAccount is not explicitly specified, OLM’s cluster-admin credentials are used. Another avenue that cluster administrators have is to lock down permissions and usage of the CatalogSource API, disable default catalogs, and provide tenants with custom vetted catalogs. However if a cluster admin is not aware of these options, the default configuration of a cluster results in users with permission to create a Subscription in namespaces that contain an OperatorGroup effectively have cluster-admin, because OLMv0 has unlimited permissions to install any bundle available in the default catalogs and the default community catalog is not vetted for limited RBAC. Because OLMv0 is used to install more RBAC and run arbitrary workloads, there are numerous potential vectors that attackers could exploit. While there are no known exploits and there has not been any specific concern reported from customers, we believe CNCF’s reputation rest on secure cloud-native software and that this is a non-negotiable area to improve. To make OLM secure by default: -- OLMv1 will not be granted cluster admin permissions. Instead it will require service accounts provided by users to actually install, upgrade, and delete content. In addition to the security this provides, it also fulfills one of OLM’s long-standing requirements: halt when bundle upgrades require additional permissions and wait until those permissions are granted. -- OLMv1 will use secure communication protocols between all internal components and between itself and its clients. + +- OLM v1 will not be granted cluster admin permissions. Instead, it will require service accounts provided by users to actually install, upgrade, and delete content. In addition to the security this provides, it also fulfills one of OLM’s long-standing requirements: halt when bundle upgrades require additional permissions and wait until those permissions are granted. +- OLM v1 will use secure communication protocols between all internal components and between itself and its clients. ### Simple and predictable semantics for install, upgrade, and delete OLMv0 has grown into a complex web of functionality that is difficult to understand, even for seasoned Kubernetes veterans. -In OLMv1 we will move to GitOps-friendly APIs that allow administrators to rely on their experience with conventional Kubernetes API behavior (declarative, eventually consistent) to manage operator lifecycles. +In OLM v1 we will move to GitOps-friendly APIs that allow administrators to rely on their experience with conventional Kubernetes API behavior (declarative, eventually consistent) to manage operator lifecycles. -OLMv1 will reduce its API surface down to two primary APIs that represent catalogs of content, and intent for that content to be installed on the cluster. +OLM v1 will reduce its API surface down to two primary APIs that represent catalogs of content, and intent for that content to be installed on the cluster. + +OLM v1 will: -OLMv1 will: - Permit administrators to pin to specific versions, channels, version ranges, or combinations of both. - Permit administrators to pause management of an installation for maintenance or troubleshooting purposes. - Put opinionated guardrails up by default (e.g. follow operator developer-defined upgrade edges). @@ -188,53 +203,58 @@ OLMv1 will: ### APIs and behaviors to handle common controller patterns OLMv0 takes an extremely opinionated stance on the contents of the bundles it installs and in the way that operators can be lifecycled. The original designers believed these opinions would keep OLM’s scope limited and that they encompassed best practices for operator lifecycling. Some of these opinions are: + - All bundles must include a ClusterServiceVersion, which ostensibly gives operator authors an API that they can use to fully describe how to run the operator, what permissions it requires, what APIs it provides, and what metadata to show to users. - Bundles cannot contain arbitrary objects. OLMv0 needs to have specific handling for each resource that it supports. - Cluster administrators cannot override OLM safety checks around CRD changes or upgrades. -OLMv1 will take a slightly different approach: -- It will not require bundles to have very specific controller-centric shapes. OLMv1 will happily install a bundle that contains a deployment, service, and ingress or a bundle that contains a single configmap. +OLM v1 will take a slightly different approach: + +- It will not require bundles to have very specific controller-centric shapes. OLM v1 will happily install a bundle that contains a deployment, service, and ingress or a bundle that contains a single configmap. - However for bundles that do include CRDs, controllers, RBAC, webhooks, and other objects that relate to the behavior of the apiserver, OLM will continue to have opinions and special handling: - CRD upgrade checks (best effort) - Specific knowledge and handling of webhooks. -- To the extent necessary OLMv1 will include optional controller-centric concepts in its APIs and or CLIs in order to facilitate the most common controller patterns. Examples could include: +- To the extent necessary OLM v1 will include optional controller-centric concepts in its APIs and or CLIs in order to facilitate the most common controller patterns. Examples could include: - Permission management - CRD upgrade check policies -- OLMv1 will continue to have opinions about upgrade traversals and CRD changes that help users prevent accidental breakage, but it will also allow administrators to disable safeguards and proceed anyway. +- OLM v1 will continue to have opinions about upgrade traversals and CRD changes that help users prevent accidental breakage, but it will also allow administrators to disable safeguards and proceed anyway. -OLMv0 has some support for automatic upgrades. However administrators cannot control the maximum version for automatic upgrades, and the upgrade policy (manual vs automatic) applies to all operators in a namespace. If any operator’s upgrade policy is manual, all upgrades of all operators in the namespace must be approved manually. +OLMv0 has some support for automatic upgrades. However, administrators cannot control the maximum version for automatic upgrades, and the upgrade policy (manual vs automatic) applies to all operators in a namespace. If any operator’s upgrade policy is manual, all upgrades of all operators in the namespace must be approved manually. -OLMv1 will have fine-grained control for version ranges (and pins) and for controlling automatic upgrades for individual operators regardless of the policy of other operators installed in the same namespace. +OLM v1 will have fine-grained control for version ranges (and pins) and for controlling automatic upgrades for individual operators regardless of the policy of other operators installed in the same namespace. ### Constraint checking (but not automated on-cluster management) OLMv0 includes support for dependency and constraint checking for many common use cases (e.g. required and provided APIs, required cluster version, required package versions). It also has other constraint APIs that have not gained traction (e.g. CEL expressions and compound constraints). In addition to its somewhat hap-hazard constraint expression support, OLMv0 also automatically installs dependency trees, which has proven problematic in several respects: + 1. OLMv0 can resolve existing dependencies from outside the current namespace, but it can only install new dependencies in the current namespace. One scenario where this is problematic is if A depends on B, where A supports only OwnNamespace mode and B supports only AllNamespace mode. In that case, OLMv0’s auto dependency management fails because B cannot be installed in the same namespace as A (assuming the OperatorGroup in that namespace is configured for OwnNamespace operators to work). 2. OLMv0’s logic for choosing a dependency among multiple contenders is confusing and error-prone, and an administrator’s ability to have fine-grained control of upgrades is essentially limited to building and deploying tailor-made catalogs. 3. OLMv0 automatically installs dependencies. The only way for an administrator to avoid this OLMv0 functionality is to fully understand the dependency tree in advance and to then install dependencies from the leaves to the root so that OLMv0 always detects that dependencies are already met. If OLMv0 installs a dependency for you, it does not uninstall it when it is no longer depended upon. -OLMv1 will not provide dependency resolution among packages in the catalog (see [Dependencies based on watched namespaces](#dependencies-based-on-watched-namespaces)) +OLM v1 will not provide dependency resolution among packages in the catalog (see [Dependencies based on watched namespaces](#dependencies-based-on-watched-namespaces)) -OLMv1 will provide constraint checking based on available cluster state. Constraint checking will be limited to checking whether the existing constraints are met. If so, install proceeds. If not, unmet constraints will be reported and the install/upgrade waits until constraints are met. +OLM v1 will provide constraint checking based on available cluster state. Constraint checking will be limited to checking whether the existing constraints are met. If so, install proceeds. If not, unmet constraints will be reported and the install/upgrade waits until constraints are met. -The Operator Framework team will perform a survey of registry+v1 packages that currently rely on OLMv0’s dependency features and will suggest a solution as part of the overall OLMv0 to OLMv1 migration effort. +The Operator Framework team will perform a survey of registry+v1 packages that currently rely on OLMv0’s dependency features and will suggest a solution as part of the overall OLMv0 to OLM v1 migration effort. ### Client libraries and CLIs contribute to the overall UX OLMv0 has no official client libraries or CLIs that can be used to augment its functionality or provide a more streamlined user experience. The kubectl "operator" plugin was developed several years ago, but has never been a focus of the core Operator Framework development team, and has never factored into the overall architecture. -OLMv1 will deliver an official CLI (likely by overhauling the kubectl operator plugin) and will use it to meet requirements that are difficult or impossible to implement in a controller, or where an architectural assessment dictates that a client is the better choice. This CLI would automate standard workflows over cluster APIs to facilitate simple administrative actions (e.g. automatically create RBAC and ServiceAccounts necessary for an extension installation as an optional step in the CLI’s extension install experience). +OLM v1 will deliver an official CLI (likely by overhauling the kubectl operator plugin) and will use it to meet requirements that are difficult or impossible to implement in a controller, or where an architectural assessment dictates that a client is the better choice. This CLI would automate standard workflows over cluster APIs to facilitate simple administrative actions (e.g. automatically create RBAC and ServiceAccounts necessary for an extension installation as an optional step in the CLI’s extension install experience). The official CLI will provide administrators and users with a UX that covers the most common scenarios users will encounter. -The official CLI will explicitly NOT attempt to cover complex scenarios. Maintainers will reject requests to over-complicate the CLI. Users with advanced use cases will be able to directly interact with OLMv1’s on-cluster APIs. +The official CLI will explicitly NOT attempt to cover complex scenarios. Maintainers will reject requests to over-complicate the CLI. Users with advanced use cases will be able to directly interact with OLM v1’s on-cluster APIs. The idea is: + - On-cluster APIs can be used to manage operators in 100% of cases (assuming bundle content is structured in a compatible way) - The official CLI will cover standard user flows, covering ~80% of use cases. - Third-party or unofficial CLIs will cover the remaining ~20% of use cases. Areas where the official CLI could provide value include: + - Catalog interactions (search, list, inspect, etc.) - Standard install/upgrade/delete commands - Upgrade previews diff --git a/docs/refs/supported-extensions.md b/docs/project/olmv1_limitations.md similarity index 85% rename from docs/refs/supported-extensions.md rename to docs/project/olmv1_limitations.md index 8a1e97c029..172d8cbb5f 100644 --- a/docs/refs/supported-extensions.md +++ b/docs/project/olmv1_limitations.md @@ -1,8 +1,15 @@ +--- +hide: + - toc +--- + +## OLM v0 Extension Support + Currently, OLM v1 supports installing cluster extensions that meet the following criteria: * The extension must support installation via the `AllNamespaces` install mode. * The extension must not use webhooks. -* The extension must not declare dependencies using the any of following file-based catalog properties: +* The extension must not declare dependencies using any of the following file-based catalog properties: * `olm.gvk.required` * `olm.package.required` diff --git a/docs/olmv1_roadmap.md b/docs/project/olmv1_roadmap.md similarity index 99% rename from docs/olmv1_roadmap.md rename to docs/project/olmv1_roadmap.md index 23bcc5d96e..5a0542a3e5 100644 --- a/docs/olmv1_roadmap.md +++ b/docs/project/olmv1_roadmap.md @@ -1,7 +1,6 @@ --- -title: Product Requriement Doc -layout: default -nav_order: 2 +hide: + - toc --- # OLM v1 roadmap diff --git a/docs/Tasks/adding-a-catalog.md b/docs/tutorials/add-catalog.md similarity index 98% rename from docs/Tasks/adding-a-catalog.md rename to docs/tutorials/add-catalog.md index 8158f0d4a7..c0961d5616 100644 --- a/docs/Tasks/adding-a-catalog.md +++ b/docs/tutorials/add-catalog.md @@ -1,4 +1,9 @@ -# Adding a catalog of extensions to a cluster +--- +hide: + - toc +--- + +# Add a Catalog of Extensions to a Cluster Extension authors can publish their products in catalogs. ClusterCatalogs are curated collections of Kubernetes extensions, such as Operators. diff --git a/docs/drafts/downgrading-an-extension.md b/docs/tutorials/downgrade-extension.md similarity index 99% rename from docs/drafts/downgrading-an-extension.md rename to docs/tutorials/downgrade-extension.md index c372ce8e2e..0e57d46873 100644 --- a/docs/drafts/downgrading-an-extension.md +++ b/docs/tutorials/downgrade-extension.md @@ -1,3 +1,7 @@ +--- +hide: + - toc +--- # Downgrade a ClusterExtension diff --git a/docs/Tasks/exploring-available-packages.md b/docs/tutorials/explore-available-content.md similarity index 96% rename from docs/Tasks/exploring-available-packages.md rename to docs/tutorials/explore-available-content.md index eb3e1499aa..2364501c11 100644 --- a/docs/Tasks/exploring-available-packages.md +++ b/docs/tutorials/explore-available-content.md @@ -1,6 +1,11 @@ -# Exploring Available Packages +--- +hide: + - toc +--- -After you add a catalog of extensions to your cluster, you must port forward your catalog as a service. +# Explore Available Content + +After you [add a catalog of extensions](add-catalog.md) to your cluster, you must port forward your catalog as a service. Then you can query the catalog by using `curl` commands and the `jq` CLI tool to find extensions to install. ## Prerequisites @@ -143,4 +148,4 @@ The following examples will show this default behavior, but for simplicity's sak ### Additional resources -* [Catalog queries](../refs/catalog-queries.md) +* [Catalog queries](../howto/catalog-queries.md) diff --git a/docs/Tasks/installing-an-extension.md b/docs/tutorials/install-extension.md similarity index 80% rename from docs/Tasks/installing-an-extension.md rename to docs/tutorials/install-extension.md index 1458a1653e..ffd28519f3 100644 --- a/docs/Tasks/installing-an-extension.md +++ b/docs/tutorials/install-extension.md @@ -1,4 +1,9 @@ -# Installing an extension from a catalog +--- +hide: + - toc +--- + +# Install an Extension from a Catalog In Operator Lifecycle Manager (OLM) 1.0, Kubernetes extensions are scoped to the cluster. After you add a catalog to your cluster, you can install an extension by creating a custom resource (CR) and applying it. @@ -6,9 +11,22 @@ After you add a catalog to your cluster, you can install an extension by creatin ## Prerequisites * A deployed and unpacked catalog -* The name, and optionally version, or channel, of the [supported extension](../concepts/supported-extensions.md) to be installed +* The name, and optionally version, or channel, of the [supported extension](../project/olmv1_limitations.md) to be installed * An existing namespace in which to install the extension -* A suitable service account for installation (more information can be found [here](../drafts/Tasks/create-installer-service-account.md)) + +### ServiceAccount for ClusterExtension Installation and Management + +Adhering to OLM v1's "Secure by Default" tenet, OLM v1 does not have the permissions +necessary to install content. This follows the least privilege principle and reduces +the chance of a [confused deputy attack](https://en.wikipedia.org/wiki/Confused_deputy_problem). +Instead, users must explicitly specify a ServiceAccount that will be used to perform the +installation and management of a specific ClusterExtension. + +The ServiceAccount must be configured with the RBAC permissions required by the ClusterExtension. +If the permissions do not meet the minimum requirements, installation will fail. If no ServiceAccount +is provided in the ClusterExtension manifest, then the manifest will be rejected. + +For information on determining the ServiceAccount's permission, please see [Derive minimal ServiceAccount required for ClusterExtension Installation and Management](../howto/derive-service-account.md). ## Procedure diff --git a/docs/Tasks/uninstalling-an-extension.md b/docs/tutorials/uninstall-extension.md similarity index 94% rename from docs/Tasks/uninstalling-an-extension.md rename to docs/tutorials/uninstall-extension.md index 575a7602aa..3d20442a82 100644 --- a/docs/Tasks/uninstalling-an-extension.md +++ b/docs/tutorials/uninstall-extension.md @@ -1,4 +1,9 @@ -# Deleting an extension +--- +hide: + - toc +--- + +# Uninstall an extension You can uninstall a Kubernetes extension and its associated custom resource definitions (CRD) by deleting the extension's custom resource (CR). diff --git a/docs/drafts/Tasks/upgrading-an-extension.md b/docs/tutorials/upgrade-extension.md similarity index 94% rename from docs/drafts/Tasks/upgrading-an-extension.md rename to docs/tutorials/upgrade-extension.md index ec13c73178..e55a53f963 100644 --- a/docs/drafts/Tasks/upgrading-an-extension.md +++ b/docs/tutorials/upgrade-extension.md @@ -1,17 +1,22 @@ -# Upgrading an Extension +--- +hide: + - toc +--- + +# Upgrade an Extension Existing extensions can be upgraded by updating the version field in the ClusterExtension resource. -For information on downgrading an extension, see [Downgrade an Extension](../downgrading-an-extension.md). +For information on downgrading an extension, see [Downgrade an Extension](downgrade-extension.md). ## Prerequisites * You have an extension installed -* The target version is compatible with OLM v1 (see [OLM v1 limitations](../refs/olmv1-limitations.md)) -* CRD compatibility between the versions being upgraded or downgraded (see [CRD upgrade safety](../../refs/crd-upgrade-safety.md)) -* The installer service account's RBAC permissions are adequate for the target version (see [Minimal RBAC for Installer Service Account](create-installer-service-account.md)) +* The target version is compatible with OLM v1 (see [OLM v1 limitations](../project/olmv1_limitations.md)) +* CRD compatibility between the versions being upgraded or downgraded (see [CRD upgrade safety](../concepts/crd-upgrade-safety.md)) +* The installer service account's RBAC permissions are adequate for the target version (see [Minimal RBAC for Installer Service Account](../howto/derive-service-account.md)) -For more detailed information see [Upgrade Support](../upgrade-support.md). +For more detailed information see [Upgrade Support](../concepts/upgrade-support.md). ## Procedure diff --git a/mkdocs.yml b/mkdocs.yml index cc95662a31..7680fd461f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,28 +1,57 @@ # yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json -site_name: Operator Controller documentation +site_name: Operator Lifecycle Manager theme: - name: "material" - features: - - content.code.copy + logo: assets/logo.svg + name: "material" + palette: + primary: black + features: + - content.code.copy + - navigation.top +# - navigation.tabs + - navigation.indexes repo_url: https://github.com/operator-framework/operator-controller +extra_css: + - css/extra.css + nav: - - Home: 'index.md' - - Components: 'components.md' - - Tasks: - - Adding a catalog of extensions: 'Tasks/adding-a-catalog.md' - - Finding extensions to install: 'Tasks/exploring-available-packages.md' - - Installing an extension: 'Tasks/installing-an-extension.md' - - Deleting an extension: 'Tasks/uninstalling-an-extension.md' - - References: - - Supported extensions: 'refs//supported-extensions.md' - - API references: - - Operator Controller API reference: 'refs/api/operator-controller-api-reference.md' - - CatalogD API reference: 'refs/api/catalogd-api-reference.md' - - Catalog queries: 'refs/catalog-queries.md' - - CRD Upgrade Safety: 'refs/crd-upgrade-safety.md' + - Overview: + - index.md + - Community: project/olmv1_community.md + - Architecture: project/olmv1_architecture.md + - Design Decisions: project/olmv1_design_decisions.md + - Limitations: project/olmv1_limitations.md + - Roadmap: project/olmv1_roadmap.md + - Getting Started: getting-started/olmv1_getting_started.md + - Tutorials: + - Add a Catalog: tutorials/add-catalog.md + - Explore Content: tutorials/explore-available-content.md + - Install an Extension: tutorials/install-extension.md + - Upgrade an Extension: tutorials/upgrade-extension.md + - Downgrade an Extension: tutorials/downgrade-extension.md + - Uninstall an Extension: tutorials/uninstall-extension.md + - How-To Guides: + - Catalog queries: howto/catalog-queries.md + - Channel-Based Upgrades: howto/how-to-channel-based-upgrades.md + - Version Pinning: howto/how-to-pin-version.md + - Version Range Upgrades: howto/how-to-version-range-upgrades.md + - Z-Stream Upgrades: howto/how-to-z-stream-upgrades.md + - Derive Service Account Permissions: howto/derive-service-account.md + - Conceptual Guides: + - Single Owner Objects: concepts/single-owner-objects.md + - Upgrade Support: concepts/upgrade-support.md + - CRD Upgrade Safety: concepts/crd-upgrade-safety.md + - Content Resolution: concepts/controlling-catalog-selection.md + - Version Ranges: concepts/version-ranges.md + - API Reference: + - Operator Controller API reference: api-reference/operator-controller-api-reference.md + - CatalogD API reference: api-reference/catalogd-api-reference.md + - Contribute: + - Contributing: contribute/contributing.md + - Developing OLM v1: contribute/developer.md markdown_extensions: - pymdownx.highlight: From 2d4c0003a462e1946af32575781a3c71f516ed03 Mon Sep 17 00:00:00 2001 From: Jordan Keister Date: Fri, 4 Oct 2024 04:33:19 -0500 Subject: [PATCH 052/694] Add testify linter and address fixes (#1334) While reviewing https://github.com/operator-framework/operator-controller/pull/1330 we had a discussion about the ability to generally prevent the use of `requires` within `Eventually` calls to prevent future flakes, and that led us to evaluate the `testifylint`-er to give us some cautionary guidelines. It did _not_ resolve the requires-within-eventually issue, but we may try to tackle that with some custom AST in the future. Signed-off-by: Jordan Keister --- .golangci.yaml | 1 + internal/action/helm_test.go | 5 +- internal/catalogmetadata/cache/cache_test.go | 18 +- .../catalogmetadata/client/client_test.go | 11 +- .../filter/bundle_predicates_test.go | 3 +- .../catalogmetadata/filter/successors_test.go | 6 +- .../clusterextension_controller_test.go | 2 +- internal/resolve/catalog_test.go | 8 +- .../rukpak/source/containers_image_test.go | 20 +-- test/e2e/cluster_extension_install_test.go | 154 ++++++++---------- 10 files changed, 106 insertions(+), 122 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 6ccb7c35d7..1ecb409944 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -30,6 +30,7 @@ linters: - nonamedreturns - prealloc - stylecheck + - testifylint - tparallel - unconvert - unparam diff --git a/internal/action/helm_test.go b/internal/action/helm_test.go index 8979f18311..d07dc53bcd 100644 --- a/internal/action/helm_test.go +++ b/internal/action/helm_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/release" "sigs.k8s.io/controller-runtime/pkg/client" @@ -130,12 +131,12 @@ func TestActionClientFor(t *testing.T) { // Test the successful case actionClient, err := acg.ActionClientFor(ctx, obj) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, actionClient) assert.IsType(t, &ActionClient{}, actionClient) // Test the error case actionClient, err = acg.ActionClientFor(ctx, obj) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, actionClient) } diff --git a/internal/catalogmetadata/cache/cache_test.go b/internal/catalogmetadata/cache/cache_test.go index f898e23e32..e40009ccfe 100644 --- a/internal/catalogmetadata/cache/cache_test.go +++ b/internal/catalogmetadata/cache/cache_test.go @@ -79,18 +79,18 @@ func TestFilesystemCachePutAndGet(t *testing.T) { t.Log("Get empty v1 cache") actualFSGet, err := c.Get(catalogName, resolvedRef1) - assert.NoError(t, err) + require.NoError(t, err) assert.Nil(t, actualFSGet) t.Log("Put v1 content into cache") actualFSPut, err := c.Put(catalogName, resolvedRef1, defaultContent(), nil) - assert.NoError(t, err) + require.NoError(t, err) require.NotNil(t, actualFSPut) - assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) + require.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) t.Log("Get v1 content from cache") actualFSGet, err = c.Get(catalogName, resolvedRef1) - assert.NoError(t, err) + require.NoError(t, err) require.NotNil(t, actualFSGet) assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) assert.NoError(t, equalFilesystems(actualFSPut, actualFSGet)) @@ -98,7 +98,7 @@ func TestFilesystemCachePutAndGet(t *testing.T) { t.Log("Put v1 error into cache") actualFSPut, err = c.Put(catalogName, resolvedRef1, nil, errors.New("fake put error")) // Errors do not override previously successfully populated cache - assert.NoError(t, err) + require.NoError(t, err) require.NotNil(t, actualFSPut) assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) assert.NoError(t, equalFilesystems(actualFSPut, actualFSGet)) @@ -115,13 +115,13 @@ func TestFilesystemCachePutAndGet(t *testing.T) { t.Log("Put v2 content into cache") actualFSPut, err = c.Put(catalogName, resolvedRef2, defaultContent(), nil) - assert.NoError(t, err) + require.NoError(t, err) require.NotNil(t, actualFSPut) - assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) + require.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) t.Log("Get v2 content from cache") actualFSGet, err = c.Get(catalogName, resolvedRef2) - assert.NoError(t, err) + require.NoError(t, err) require.NotNil(t, actualFSGet) assert.NoError(t, equalFilesystems(defaultFS(), actualFSPut)) assert.NoError(t, equalFilesystems(actualFSPut, actualFSGet)) @@ -130,7 +130,7 @@ func TestFilesystemCachePutAndGet(t *testing.T) { // Cache should be empty and no error because // Put with a new version overrides the old version actualFSGet, err = c.Get(catalogName, resolvedRef1) - assert.NoError(t, err) + require.NoError(t, err) assert.Nil(t, actualFSGet) } diff --git a/internal/catalogmetadata/client/client_test.go b/internal/catalogmetadata/client/client_test.go index a1cec5d4ae..d4787a7f95 100644 --- a/internal/catalogmetadata/client/client_test.go +++ b/internal/catalogmetadata/client/client_test.go @@ -11,6 +11,7 @@ import ( "testing/fstest" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" @@ -77,7 +78,7 @@ func TestClientGetPackage(t *testing.T) { pkgName: "pkg-missing", cache: &fakeCache{getFS: testFS}, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, &declcfg.DeclarativeConfig{}, fbc) }, }, @@ -89,7 +90,7 @@ func TestClientGetPackage(t *testing.T) { "invalid-pkg-present/olm.package/invalid-pkg-present.json": &fstest.MapFile{Data: []byte(`{"schema": "olm.package","name": 12345}`)}, }}, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { - assert.ErrorContains(t, err, `error loading package "invalid-pkg-present"`) + require.ErrorContains(t, err, `error loading package "invalid-pkg-present"`) assert.Nil(t, fbc) }, }, @@ -99,7 +100,7 @@ func TestClientGetPackage(t *testing.T) { pkgName: "pkg-present", cache: &fakeCache{getFS: testFS}, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, &declcfg.DeclarativeConfig{Packages: []declcfg.Package{{Schema: declcfg.SchemaPackage, Name: "pkg-present"}}}, fbc) }, }, @@ -111,7 +112,7 @@ func TestClientGetPackage(t *testing.T) { return testFS, nil }}, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, &declcfg.DeclarativeConfig{Packages: []declcfg.Package{{Schema: declcfg.SchemaPackage, Name: "pkg-present"}}}, fbc) }, }, @@ -170,7 +171,7 @@ func TestClientPopulateCache(t *testing.T) { }, nil }, assert: func(t *testing.T, fs fs.FS, err error) { - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, testFS, fs) }, putFuncConstructor: func(t *testing.T) func(source string, errToCache error) (fs.FS, error) { diff --git a/internal/catalogmetadata/filter/bundle_predicates_test.go b/internal/catalogmetadata/filter/bundle_predicates_test.go index dd6abe6d7c..beb2950f19 100644 --- a/internal/catalogmetadata/filter/bundle_predicates_test.go +++ b/internal/catalogmetadata/filter/bundle_predicates_test.go @@ -6,6 +6,7 @@ import ( mmsemver "github.com/Masterminds/semver/v3" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" @@ -40,7 +41,7 @@ func TestInMastermindsSemverRange(t *testing.T) { } vRange, err := mmsemver.NewConstraint(">=1.0.0") - assert.NoError(t, err) + require.NoError(t, err) f := filter.InMastermindsSemverRange(vRange) diff --git a/internal/catalogmetadata/filter/successors_test.go b/internal/catalogmetadata/filter/successors_test.go index 1d7cc72c39..1b27f9778f 100644 --- a/internal/catalogmetadata/filter/successors_test.go +++ b/internal/catalogmetadata/filter/successors_test.go @@ -171,7 +171,7 @@ func TestSuccessorsPredicateWithForceSemverUpgradeConstraintsEnabled(t *testing. } { t.Run(tt.name, func(t *testing.T) { successors, err := SuccessorsOf(tt.installedBundle, channelSet[testPackageName]) - assert.NoError(t, err) + require.NoError(t, err) allBundles := make([]declcfg.Bundle, 0, len(bundleSet)) for _, bundle := range bundleSet { @@ -328,7 +328,7 @@ func TestSuccessorsPredicateWithForceSemverUpgradeConstraintsDisabled(t *testing } { t.Run(tt.name, func(t *testing.T) { successors, err := SuccessorsOf(tt.installedBundle, channelSet[testPackageName]) - assert.NoError(t, err) + require.NoError(t, err) allBundles := make([]declcfg.Bundle, 0, len(bundleSet)) for _, bundle := range bundleSet { @@ -380,7 +380,7 @@ func TestLegacySuccessor(t *testing.T) { emptyBundle := declcfg.Bundle{} f, err := legacySuccessor(installedBundle, fakeChannel) - assert.NoError(t, err) + require.NoError(t, err) assert.True(t, f(b2)) assert.False(t, f(b3)) diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index 79f3be476c..23f81363cb 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -167,7 +167,7 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { isTerminal := errors.Is(err, reconcile.TerminalError(nil)) assert.Equal(t, tc.expectTerminal, isTerminal, "expected terminal error: %v, got: %v", tc.expectTerminal, isTerminal) - assert.ErrorContains(t, err, tc.unpackErr.Error()) + require.ErrorContains(t, err, tc.unpackErr.Error()) t.Log("By fetching updated cluster extension after reconcile") require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) diff --git a/internal/resolve/catalog_test.go b/internal/resolve/catalog_test.go index 17d0007bc0..bce3b5778e 100644 --- a/internal/resolve/catalog_test.go +++ b/internal/resolve/catalog_test.go @@ -397,7 +397,7 @@ func TestPackageVariationsBetweenCatalogs(t *testing.T) { gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) // We will not make a decision on which catalog to use - assert.ErrorContains(t, err, "in multiple catalogs with the same priority [b c]") + require.ErrorContains(t, err, "in multiple catalogs with the same priority [b c]") assert.Nil(t, gotBundle) assert.Nil(t, gotVersion) assert.Nil(t, gotDeprecation) @@ -408,7 +408,7 @@ func TestPackageVariationsBetweenCatalogs(t *testing.T) { gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) // We will not make a decision on which catalog to use - assert.ErrorContains(t, err, "in multiple catalogs with the same priority [d f]") + require.ErrorContains(t, err, "in multiple catalogs with the same priority [d f]") assert.Nil(t, gotBundle) assert.Nil(t, gotVersion) assert.Nil(t, gotDeprecation) @@ -635,7 +635,7 @@ func TestCatalogWalker(t *testing.T) { seenCatalogs = append(seenCatalogs, cat.Name) return nil } - assert.NoError(t, w(context.Background(), "", walkFunc)) + require.NoError(t, w(context.Background(), "", walkFunc)) assert.Equal(t, []string{"a", "b"}, seenCatalogs) }) } @@ -936,7 +936,7 @@ func TestMultiplePriority(t *testing.T) { ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.1", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) - assert.ErrorContains(t, err, "in multiple catalogs with the same priority [a b c]") + require.ErrorContains(t, err, "in multiple catalogs with the same priority [a b c]") assert.Nil(t, gotBundle) assert.Nil(t, gotVersion) assert.Nil(t, gotDeprecation) diff --git a/internal/rukpak/source/containers_image_test.go b/internal/rukpak/source/containers_image_test.go index 104a857214..4d793a000d 100644 --- a/internal/rukpak/source/containers_image_test.go +++ b/internal/rukpak/source/containers_image_test.go @@ -53,12 +53,12 @@ func TestUnpackValidInsecure(t *testing.T) { result, err := unpacker.Unpack(context.Background(), bundleSource) require.NoError(t, err) require.NotNil(t, result) - assert.Equal(t, result.State, source.StateUnpacked) + assert.Equal(t, source.StateUnpacked, result.State) require.NoDirExists(t, oldBundlePath) unpackedFile, err := fs.ReadFile(result.Bundle, testFileName) - assert.NoError(t, err) + require.NoError(t, err) // Ensure the unpacked file matches the source content assert.Equal(t, []byte(testFileContents), unpackedFile) assert.NoError(t, unpacker.Cleanup(context.Background(), bundleSource)) @@ -87,9 +87,9 @@ func TestUnpackValidUsesCache(t *testing.T) { // Attempt to pull and unpack the image result, err := unpacker.Unpack(context.Background(), bundleSource) - assert.NoError(t, err) + require.NoError(t, err) require.NotNil(t, result) - assert.Equal(t, result.State, source.StateUnpacked) + assert.Equal(t, source.StateUnpacked, result.State) // Make sure the original contents of the cache are still present. If the cached contents // were not used, we would expect the original contents to be removed. @@ -144,7 +144,7 @@ func TestUnpackNameOnlyImageReference(t *testing.T) { // Attempt to pull and unpack the image _, err := unpacker.Unpack(context.Background(), bundleSource) - assert.ErrorContains(t, err, "tag or digest is needed") + require.ErrorContains(t, err, "tag or digest is needed") assert.ErrorIs(t, err, reconcile.TerminalError(nil)) } @@ -226,8 +226,8 @@ func TestUnpackInvalidNilImage(t *testing.T) { // Attempt to unpack result, err := unpacker.Unpack(context.Background(), bundleSource) assert.Nil(t, result) - assert.ErrorContains(t, err, "nil image source") - assert.ErrorIs(t, err, reconcile.TerminalError(nil)) + require.ErrorContains(t, err, "nil image source") + require.ErrorIs(t, err, reconcile.TerminalError(nil)) assert.NoDirExists(t, filepath.Join(unpacker.BaseCachePath, bundleSource.Name)) } @@ -245,8 +245,8 @@ func TestUnpackInvalidImageRef(t *testing.T) { // Attempt to unpack result, err := unpacker.Unpack(context.Background(), bundleSource) assert.Nil(t, result) - assert.ErrorContains(t, err, "error parsing image reference") - assert.ErrorIs(t, err, reconcile.TerminalError(nil)) + require.ErrorContains(t, err, "error parsing image reference") + require.ErrorIs(t, err, reconcile.TerminalError(nil)) assert.NoDirExists(t, filepath.Join(unpacker.BaseCachePath, bundleSource.Name)) } @@ -322,7 +322,7 @@ func TestCleanup(t *testing.T) { // Clean up the bundle err := unpacker.Cleanup(context.Background(), bundleSource) - assert.NoError(t, err) + require.NoError(t, err) assert.NoDirExists(t, bundleDir) } diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index f3f9d230e4..18ade96771 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -274,24 +274,22 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) t.Log("By eventually installing the package successfully") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "Installed bundle") + assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle) } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "Installed bundle") - assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle) }, pollDuration, pollInterval) }) } @@ -331,12 +329,11 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) + assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [operatorhubio test-catalog]") } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) - assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [operatorhubio test-catalog]") }, pollDuration, pollInterval) } @@ -378,11 +375,10 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { ) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) t.Log("It does not allow to upgrade the ClusterExtension to a non-successor version") @@ -399,11 +395,10 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) + assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"prometheus\" matching version \"1.2.0\"", cond.Message) } - assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) - assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"prometheus\" matching version \"1.2.0\"", cond.Message) }, pollDuration, pollInterval) } @@ -436,11 +431,10 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) t.Log("It allows to upgrade the ClusterExtension to a non-successor version") @@ -453,11 +447,10 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) } @@ -489,11 +482,10 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) t.Log("It does allow to upgrade the ClusterExtension to any of the successor versions within non-zero major version") @@ -505,11 +497,10 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) } @@ -551,11 +542,10 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) // patch imageRef tag on test-catalog image with v2 image @@ -566,22 +556,20 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeServing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) }, pollDuration, pollInterval) t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) } @@ -635,11 +623,10 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) // update tag on test-catalog image with v2 image @@ -650,22 +637,20 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeServing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) }, pollDuration, pollInterval) t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) } @@ -701,12 +686,11 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "Installed bundle") } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "Installed bundle") }, pollDuration, pollInterval) t.Log("By deleting a managed resource") @@ -772,23 +756,21 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) }, pollDuration, pollInterval) t.Log("By eventually failing to install the package successfully due to insufficient ServiceAccount permissions") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonFailed, cond.Reason) + assert.Contains(ct, cond.Message, "forbidden") } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonFailed, cond.Reason) - assert.Contains(ct, cond.Message, "forbidden") }, pollDuration, pollInterval) t.Log("By fixing the ServiceAccount permissions") @@ -802,24 +784,22 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "Installed bundle") + assert.NotEmpty(ct, clusterExtension.Status.Install) } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - assert.Contains(ct, cond.Message, "Installed bundle") - assert.NotEmpty(ct, clusterExtension.Status.Install) }, pollDuration, pollInterval) t.Log("By eventually reporting Progressing == False with Reason Success") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) - if !assert.NotNil(ct, cond) { - return + if assert.NotNil(ct, cond) { + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) }, pollDuration, pollInterval) } From 8466890be564f77b406640f359a942dd5123a51d Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:44:23 +0200 Subject: [PATCH 053/694] Fix a flake in upgrade e2e (#1346) There is a potential for nil pointer dereference in the test. This change adds condition to check for nil. Signed-off-by: Mikalai Radchuk --- test/upgrade-e2e/post_upgrade_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go index 8bb2ae3df6..382da5cbf2 100644 --- a/test/upgrade-e2e/post_upgrade_test.go +++ b/test/upgrade-e2e/post_upgrade_test.go @@ -86,7 +86,7 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") - if assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle) { + if assert.NotNil(ct, clusterExtension.Status.Install) { assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle.Version) } }, time.Minute, time.Second) From e57525ecfcd214dc6768d0e937a5776ba1927d79 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Fri, 4 Oct 2024 09:41:02 -0400 Subject: [PATCH 054/694] bump catalogd to v0.30.0 (#1329) Signed-off-by: Joe Lanford --- config/samples/catalogd_operatorcatalog.yaml | 2 +- docs/api-reference/catalogd-api-reference.md | 23 ++++++------ .../concepts/controlling-catalog-selection.md | 6 +-- docs/getting-started/olmv1_getting_started.md | 8 ++-- docs/tutorials/add-catalog.md | 37 +++++++++++-------- docs/tutorials/install-extension.md | 2 +- go.mod | 4 +- go.sum | 8 ++-- hack/tools/catalogs/README.md | 2 +- hack/tools/catalogs/download-catalog | 8 ++-- internal/catalogmetadata/cache/cache.go | 10 ++--- internal/catalogmetadata/client/client.go | 10 ++--- .../catalogmetadata/client/client_test.go | 16 ++++---- .../clusterextension_controller.go | 2 +- test/upgrade-e2e/post_upgrade_test.go | 2 +- 15 files changed, 73 insertions(+), 67 deletions(-) diff --git a/config/samples/catalogd_operatorcatalog.yaml b/config/samples/catalogd_operatorcatalog.yaml index 5965edeb1f..48f1da5734 100644 --- a/config/samples/catalogd_operatorcatalog.yaml +++ b/config/samples/catalogd_operatorcatalog.yaml @@ -4,7 +4,7 @@ metadata: name: operatorhubio spec: source: - type: image + type: Image image: ref: quay.io/operatorhubio/catalog:latest pollInterval: 10m diff --git a/docs/api-reference/catalogd-api-reference.md b/docs/api-reference/catalogd-api-reference.md index 07be6f3c64..80a4b327da 100644 --- a/docs/api-reference/catalogd-api-reference.md +++ b/docs/api-reference/catalogd-api-reference.md @@ -27,6 +27,7 @@ Package v1alpha1 contains API Schema definitions for the core v1alpha1 API group CatalogSource is a discriminated union of possible sources for a Catalog. +CatalogSource contains the sourcing information for a Catalog @@ -35,8 +36,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `type` _[SourceType](#sourcetype)_ | type is a required reference to the type of source the catalog is sourced from.

Allowed values are ["image"]

When this field is set to "image", the ClusterCatalog content will be sourced from an OCI image.
When using an image source, the image field must be set and must be the only field defined for this type. | | Enum: [image]
Required: \{\}
| -| `image` _[ImageSource](#imagesource)_ | image is used to configure how catalog contents are sourced from an OCI image. This field must be set when type is set to "image" and must be the only field defined for this type. | | | +| `type` _[SourceType](#sourcetype)_ | type is a required reference to the type of source the catalog is sourced from.

Allowed values are ["Image"]

When this field is set to "Image", the ClusterCatalog content will be sourced from an OCI image.
When using an image source, the image field must be set and must be the only field defined for this type. | | Enum: [Image]
Required: \{\}
| +| `image` _[ImageSource](#imagesource)_ | image is used to configure how catalog contents are sourced from an OCI image. This field must be set when type is set to "Image" and must be the only field defined for this type. | | | #### ClusterCatalog @@ -95,7 +96,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `source` _[CatalogSource](#catalogsource)_ | source is a required field that allows the user to define the source of a Catalog that contains catalog metadata in the File-Based Catalog (FBC) format.

Below is a minimal example of a ClusterCatalogSpec that sources a catalog from an image:

source:
type: image
image:
ref: quay.io/operatorhubio/catalog:latest

For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs | | | +| `source` _[CatalogSource](#catalogsource)_ | source is a required field that allows the user to define the source of a Catalog that contains catalog metadata in the File-Based Catalog (FBC) format.

Below is a minimal example of a ClusterCatalogSpec that sources a catalog from an image:

source:
type: Image
image:
ref: quay.io/operatorhubio/catalog:latest

For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs | | | | `priority` _integer_ | priority is an optional field that allows the user to define a priority for a ClusterCatalog.
A ClusterCatalog's priority is used by clients as a tie-breaker between ClusterCatalogs that meet the client's requirements.
For example, in the case where multiple ClusterCatalogs provide the same bundle.
A higher number means higher priority. Negative number as also accepted.
When omitted, the default priority is 0. | 0 | | @@ -113,10 +114,9 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterCatalog.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human-readable message that further elaborates on the state of the condition.

The current set of condition types are:
- "Unpacked", epresents whether, or not, the catalog contents have been successfully unpacked.
- "Deleted", represents whether, or not, the catalog contents have been successfully deleted.

The current set of reasons are:
- "UnpackPending", this reason is set on the "Unpack" condition when unpacking the catalog has not started.
- "Unpacking", this reason is set on the "Unpack" condition when the catalog is being unpacked.
- "UnpackSuccessful", this reason is set on the "Unpack" condition when unpacking the catalog is successful and the catalog metadata is available to the cluster.
- "FailedToStore", this reason is set on the "Unpack" condition when an error has been encountered while storing the contents of the catalog.
- "FailedToDelete", this reason is set on the "Delete" condition when an error has been encountered while deleting the contents of the catalog. | | | -| `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source based on the source type.

Below is an example of a resolved source for an image source:
resolvedSource:

image:
lastPollAttempt: "2024-09-10T12:22:13Z"
lastUnpacked: "2024-09-10T12:22:13Z"
ref: quay.io/operatorhubio/catalog:latest
resolvedRef: quay.io/operatorhubio/catalog@sha256:c7392b4be033da629f9d665fec30f6901de51ce3adebeff0af579f311ee5cf1b
type: image | | | +| `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source based on the source type.

Below is an example of a resolved source for an image source:
resolvedSource:

image:
lastPollAttempt: "2024-09-10T12:22:13Z"
lastUnpacked: "2024-09-10T12:22:13Z"
ref: quay.io/operatorhubio/catalog:latest
resolvedRef: quay.io/operatorhubio/catalog@sha256:c7392b4be033da629f9d665fec30f6901de51ce3adebeff0af579f311ee5cf1b
type: Image | | | | `contentURL` _string_ | contentURL is a cluster-internal URL from which on-cluster components
can read the content of a catalog | | | -| `observedGeneration` _integer_ | observedGeneration is the most recent generation observed for this ClusterCatalog. It corresponds to the
ClusterCatalog's generation, which is updated on mutation by the API Server. | | | -| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastUnpacked represents the time when the
ClusterCatalog object was last unpacked. | | | +| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastUnpacked represents the time when the
ClusterCatalog object was last unpacked successfully. | | | #### ImageSource @@ -141,6 +141,7 @@ _Appears in:_ ResolvedCatalogSource is a discriminated union of resolution information for a Catalog. +ResolvedCatalogSource contains the information about a sourced Catalog @@ -149,7 +150,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `type` _[SourceType](#sourcetype)_ | type is a reference to the type of source the catalog is sourced from.

It will be set to one of the following values: ["image"].

When this field is set to "image", information about the resolved image source will be set in the 'image' field. | | Enum: [image]
Required: \{\}
| +| `type` _[SourceType](#sourcetype)_ | type is a reference to the type of source the catalog is sourced from.

It will be set to one of the following values: ["Image"].

When this field is set to "Image", information about the resolved image source will be set in the 'image' field. | | Enum: [Image]
Required: \{\}
| | `image` _[ResolvedImageSource](#resolvedimagesource)_ | image is a field containing resolution information for a catalog sourced from an image. | | | @@ -166,10 +167,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `ref` _string_ | ref is the reference to a container image containing Catalog contents. | | | -| `resolvedRef` _string_ | resolvedRef is the resolved sha256 image ref containing Catalog contents. | | | -| `lastPollAttempt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastPollAttempt is the time when the source image was last polled for new content. | | | -| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastUnpacked is the last time when the Catalog contents were successfully unpacked. | | | +| `ref` _string_ | ref contains the resolved sha256 image ref containing Catalog contents. | | | +| `lastSuccessfulPollAttempt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastSuccessfulPollAttempt is the time when the resolved source was last successfully polled for new content. | | | #### SourceType @@ -186,6 +185,6 @@ _Appears in:_ | Field | Description | | --- | --- | -| `image` | | +| `Image` | | diff --git a/docs/concepts/controlling-catalog-selection.md b/docs/concepts/controlling-catalog-selection.md index 544f36be51..68d19c2b3e 100644 --- a/docs/concepts/controlling-catalog-selection.md +++ b/docs/concepts/controlling-catalog-selection.md @@ -132,7 +132,7 @@ metadata: spec: priority: 1000 source: - type: image + type: Image image: ref: quay.io/example/high-priority-content-management:latest ``` @@ -169,7 +169,7 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera spec: priority: 1000 source: - type: image + type: Image image: ref: quay.io/example/content-management-a:latest ``` @@ -184,7 +184,7 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera spec: priority: 500 source: - type: image + type: Image image: ref: quay.io/example/content-management-b:latest ``` diff --git a/docs/getting-started/olmv1_getting_started.md b/docs/getting-started/olmv1_getting_started.md index 77760f4fc7..1156bc968b 100644 --- a/docs/getting-started/olmv1_getting_started.md +++ b/docs/getting-started/olmv1_getting_started.md @@ -41,18 +41,18 @@ metadata: name: operatorhubio spec: source: - type: image + type: Image image: ref: quay.io/operatorhubio/catalog:latest pollInterval: 10m EOF ``` -Once the catalog is unpacked successfully, its content will be available for installation. +Once the catalog is being served, its content will be available for installation. ```bash -# Wait for the ClusterCatalog to be unpacked -kubectl wait --for=condition=Unpacked=True clustercatalog/operatorhubio --timeout=60s +# Wait for the ClusterCatalog to be ready +kubectl wait --for=condition=Serving=True clustercatalog/operatorhubio --timeout=60s ``` ### Install a Cluster Extension diff --git a/docs/tutorials/add-catalog.md b/docs/tutorials/add-catalog.md index c0961d5616..60466ed23e 100644 --- a/docs/tutorials/add-catalog.md +++ b/docs/tutorials/add-catalog.md @@ -32,7 +32,7 @@ This catalog is distributed as an image [quay.io/operatorhubio/catalog](https:// name: operatorhubio spec: source: - type: image + type: Image image: ref: pollInterval: @@ -54,10 +54,10 @@ This catalog is distributed as an image [quay.io/operatorhubio/catalog](https:// name: operatorhub spec: source: - type: image + type: Image image: ref: quay.io/operatorhubio/catalog:latest - pollInterval: 1h + pollInterval: 10m ``` 2. Apply the ClusterCatalog CR: @@ -94,7 +94,7 @@ This catalog is distributed as an image [quay.io/operatorhubio/catalog](https:// ``` terminal title="Example output" Name: operatorhubio Namespace: - Labels: + Labels: olm.operatorframework.io/metadata.name=operatorhubio Annotations: API Version: olm.operatorframework.io/v1alpha1 Kind: ClusterCatalog @@ -106,25 +106,32 @@ This catalog is distributed as an image [quay.io/operatorhubio/catalog](https:// Resource Version: 6469 UID: 2e2778cb-dda6-4645-96b7-992e8dd37503 Spec: + Priority: 0 Source: Image: - Poll Interval: 15m0s + Poll Interval: 10m0s Ref: quay.io/operatorhubio/catalog:latest - Type: image + Type: Image Status: Conditions: Last Transition Time: 2024-03-12T19:35:34Z - Message: - Reason: UnpackSuccessful + Message: Successfully unpacked and stored content from resolved source + Observed Generation: 2 + Reason: Succeeded + Status: False + Type: Progressing + Last Transition Time: 2024-03-12T19:35:34Z + Message: Serving desired content from resolved source + Observed Generation: 2 + Reason: Available Status: True - Type: Unpacked + Type: Serving Content URL: https://catalogd-server.olmv1-system.svc/catalogs/operatorhubio/all.json - Observed Generation: 2 + Last Unpacked: 2024-03-12T19:35:34Z Resolved Source: Image: - Last Poll Attempt: 2024-03-12T19:35:26Z - Ref: quay.io/operatorhubio/catalog:latest - Resolved Ref: quay.io/operatorhubio/catalog@sha256:dee29aaed76fd1c72b654b9bc8bebc4b48b34fd8d41ece880524dc0c3c1c55ec - Type: image - Events: + Last Successful Poll Attempt: 2024-03-12T19:35:26Z + Ref: quay.io/operatorhubio/catalog@sha256:dee29aaed76fd1c72b654b9bc8bebc4b48b34fd8d41ece880524dc0c3c1c55ec + Type: Image + Events: ``` diff --git a/docs/tutorials/install-extension.md b/docs/tutorials/install-extension.md index ffd28519f3..4841bb5180 100644 --- a/docs/tutorials/install-extension.md +++ b/docs/tutorials/install-extension.md @@ -10,7 +10,7 @@ After you add a catalog to your cluster, you can install an extension by creatin ## Prerequisites -* A deployed and unpacked catalog +* A catalog that is being served * The name, and optionally version, or channel, of the [supported extension](../project/olmv1_limitations.md) to be installed * An existing namespace in which to install the extension diff --git a/go.mod b/go.mod index 2af8e39b30..bf049d9d9e 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.29.0 + github.com/operator-framework/catalogd v0.30.0 github.com/operator-framework/helm-operator-plugins v0.5.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 @@ -139,7 +139,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 // indirect github.com/k14s/ytt v0.36.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.10 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect diff --git a/go.sum b/go.sum index adce30d7cf..4d792213cf 100644 --- a/go.sum +++ b/go.sum @@ -412,8 +412,8 @@ github.com/k14s/ytt v0.36.0/go.mod h1:awQ3bHBk1qT2Xn3GJVdmaLss2khZOIBBKFd2TNXZNM github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0= +github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -535,8 +535,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.29.0 h1:GpeBGUWNGbVTH9l2S4RLi2wRvLAguG/4LYdxqfrwlak= -github.com/operator-framework/catalogd v0.29.0/go.mod h1:cikCg3BwFMyMrRhMXN6cxpv03Cm/EMV3rHX5yi2w8vo= +github.com/operator-framework/catalogd v0.30.0 h1:uW5fGZ5vJlUy9n26B13WZLjc3OMrvr2gM4FQDStUd2Y= +github.com/operator-framework/catalogd v0.30.0/go.mod h1:mSyAemlhvngQPylBU1nY/57syccOv1u9biyovhlRe08= github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= diff --git a/hack/tools/catalogs/README.md b/hack/tools/catalogs/README.md index 00360a17e6..8133769bb1 100644 --- a/hack/tools/catalogs/README.md +++ b/hack/tools/catalogs/README.md @@ -33,7 +33,7 @@ If you use another container runtime, set the `CONTAINER_RUNTIME` environment va #### download-catalog -Download a catalog from an unpacked ClusterCatalog running on a cluster reachable by `kubectl`. +Download a catalog from a served ClusterCatalog running on a cluster reachable by `kubectl`. Example: diff --git a/hack/tools/catalogs/download-catalog b/hack/tools/catalogs/download-catalog index 8e997215fc..43877690eb 100755 --- a/hack/tools/catalogs/download-catalog +++ b/hack/tools/catalogs/download-catalog @@ -46,10 +46,10 @@ if [ -z "$CLUSTER_CATALOG" ]; then exit 1 fi -# Check if the Unpacked condition is true -UNPACKED_CONDITION=$(echo "$CLUSTER_CATALOG" | jq -r '.status.conditions[]? // [] | select(.type=="Unpacked") | .status') +# Check if the Serving condition is true +UNPACKED_CONDITION=$(echo "$CLUSTER_CATALOG" | jq -r '.status.conditions[]? // [] | select(.type=="Serving") | .status') if [ "$UNPACKED_CONDITION" != "True" ]; then - echo "ClusterCatalog $CATALOG_NAME is not unpacked yet." + echo "ClusterCatalog $CATALOG_NAME is not ready yet." exit 1 fi @@ -92,4 +92,4 @@ wget --no-check-certificate "$LOCAL_CONTENT_URL" -O "${CATALOG_NAME}-catalog.jso echo "Stopping kubectl port-forward..." kill $PORT_FORWARD_PID -echo "Catalog downloaded to ${CATALOG_NAME}-catalog.json" \ No newline at end of file +echo "Catalog downloaded to ${CATALOG_NAME}-catalog.json" diff --git a/internal/catalogmetadata/cache/cache.go b/internal/catalogmetadata/cache/cache.go index 70dbe51f87..e612c59cf9 100644 --- a/internal/catalogmetadata/cache/cache.go +++ b/internal/catalogmetadata/cache/cache.go @@ -28,8 +28,8 @@ func NewFilesystemCache(cachePath string) *filesystemCache { // making decisions on when to attempt to refresh // the cache. type cacheData struct { - ResolvedRef string - Error error + Ref string + Error error } // FilesystemCache is a cache that @@ -78,8 +78,8 @@ func (fsc *filesystemCache) Put(catalogName, resolvedRef string, source io.Reade cacheFS, errToCache = fsc.writeFS(catalogName, source) } fsc.cacheDataByCatalogName[catalogName] = cacheData{ - ResolvedRef: resolvedRef, - Error: errToCache, + Ref: resolvedRef, + Error: errToCache, } return cacheFS, errToCache @@ -144,7 +144,7 @@ func (fsc *filesystemCache) Get(catalogName, resolvedRef string) (fs.FS, error) func (fsc *filesystemCache) get(catalogName, resolvedRef string) (fs.FS, error) { cacheDir := fsc.cacheDir(catalogName) if data, ok := fsc.cacheDataByCatalogName[catalogName]; ok { - if resolvedRef == data.ResolvedRef { + if resolvedRef == data.Ref { if data.Error != nil { return nil, data.Error } diff --git a/internal/catalogmetadata/client/client.go b/internal/catalogmetadata/client/client.go index 50033fdb21..43b9720954 100644 --- a/internal/catalogmetadata/client/client.go +++ b/internal/catalogmetadata/client/client.go @@ -58,7 +58,7 @@ func (c *Client) GetPackage(ctx context.Context, catalog *catalogd.ClusterCatalo return nil, err } - catalogFsys, err := c.cache.Get(catalog.Name, catalog.Status.ResolvedSource.Image.ResolvedRef) + catalogFsys, err := c.cache.Get(catalog.Name, catalog.Status.ResolvedSource.Image.Ref) if err != nil { return nil, fmt.Errorf("error retrieving catalog cache: %v", err) } @@ -100,16 +100,16 @@ func (c *Client) PopulateCache(ctx context.Context, catalog *catalogd.ClusterCat if err != nil { // Any errors from the http request we want to cache // so later on cache get they can be bubbled up to the user. - return c.cache.Put(catalog.Name, catalog.Status.ResolvedSource.Image.ResolvedRef, nil, err) + return c.cache.Put(catalog.Name, catalog.Status.ResolvedSource.Image.Ref, nil, err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { errToCache := fmt.Errorf("error: received unexpected response status code %d", resp.StatusCode) - return c.cache.Put(catalog.Name, catalog.Status.ResolvedSource.Image.ResolvedRef, nil, errToCache) + return c.cache.Put(catalog.Name, catalog.Status.ResolvedSource.Image.Ref, nil, errToCache) } - return c.cache.Put(catalog.Name, catalog.Status.ResolvedSource.Image.ResolvedRef, resp.Body, nil) + return c.cache.Put(catalog.Name, catalog.Status.ResolvedSource.Image.Ref, resp.Body, nil) } func (c *Client) doRequest(ctx context.Context, catalog *catalogd.ClusterCatalog) (*http.Response, error) { @@ -136,7 +136,7 @@ func validateCatalog(catalog *catalogd.ClusterCatalog) error { return fmt.Errorf("error: provided catalog must be non-nil") } - // if the catalog has not been successfully unpacked, report an error. This ensures that our + // if the catalog is not being served, report an error. This ensures that our // reconciles are deterministic and wait for all desired catalogs to be ready. if !meta.IsStatusConditionPresentAndEqual(catalog.Status.Conditions, catalogd.TypeServing, metav1.ConditionTrue) { return fmt.Errorf("catalog %q is not being served", catalog.Name) diff --git a/internal/catalogmetadata/client/client_test.go b/internal/catalogmetadata/client/client_test.go index d4787a7f95..92645bdbfd 100644 --- a/internal/catalogmetadata/client/client_test.go +++ b/internal/catalogmetadata/client/client_test.go @@ -26,7 +26,7 @@ func defaultCatalog() *catalogd.ClusterCatalog { Status: catalogd.ClusterCatalogStatus{ Conditions: []metav1.Condition{{Type: catalogd.TypeServing, Status: metav1.ConditionTrue}}, ResolvedSource: &catalogd.ResolvedCatalogSource{Image: &catalogd.ResolvedImageSource{ - ResolvedRef: "fake/catalog@sha256:fakesha", + Ref: "fake/catalog@sha256:fakesha", }}, ContentURL: "https://fake-url.svc.local/all.json", }, @@ -47,7 +47,7 @@ func TestClientGetPackage(t *testing.T) { } for _, tc := range []testCase{ { - name: "not unpacked", + name: "not served", catalog: func() *catalogd.ClusterCatalog { return &catalogd.ClusterCatalog{ObjectMeta: metav1.ObjectMeta{Name: "catalog-1"}} }, @@ -56,7 +56,7 @@ func TestClientGetPackage(t *testing.T) { }, }, { - name: "unpacked, cache returns error", + name: "served, cache returns error", catalog: defaultCatalog, cache: &fakeCache{getErr: errors.New("fetch error")}, assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { @@ -64,7 +64,7 @@ func TestClientGetPackage(t *testing.T) { }, }, { - name: "unpacked, invalid package path", + name: "served, invalid package path", catalog: defaultCatalog, cache: &fakeCache{getFS: testFS}, pkgName: "/", @@ -73,7 +73,7 @@ func TestClientGetPackage(t *testing.T) { }, }, { - name: "unpacked, package missing", + name: "served, package missing", catalog: defaultCatalog, pkgName: "pkg-missing", cache: &fakeCache{getFS: testFS}, @@ -83,7 +83,7 @@ func TestClientGetPackage(t *testing.T) { }, }, { - name: "unpacked, invalid package present", + name: "served, invalid package present", catalog: defaultCatalog, pkgName: "invalid-pkg-present", cache: &fakeCache{getFS: fstest.MapFS{ @@ -95,7 +95,7 @@ func TestClientGetPackage(t *testing.T) { }, }, { - name: "unpacked, package present", + name: "served, package present", catalog: defaultCatalog, pkgName: "pkg-present", cache: &fakeCache{getFS: testFS}, @@ -183,7 +183,7 @@ func TestClientPopulateCache(t *testing.T) { }, }, { - name: "not unpacked", + name: "not served", catalog: func() *catalogd.ClusterCatalog { return &catalogd.ClusterCatalog{ObjectMeta: metav1.ObjectMeta{Name: "catalog-1"}} }, diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index 65cb2eaf64..2601d97f08 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -416,7 +416,7 @@ func (r *ClusterExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error { if oldObject.Status.ResolvedSource != nil && newObject.Status.ResolvedSource != nil { if oldObject.Status.ResolvedSource.Image != nil && newObject.Status.ResolvedSource.Image != nil { - return oldObject.Status.ResolvedSource.Image.ResolvedRef != newObject.Status.ResolvedSource.Image.ResolvedRef + return oldObject.Status.ResolvedSource.Image.Ref != newObject.Status.ResolvedSource.Image.Ref } } return true diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go index 382da5cbf2..f893b8e2a8 100644 --- a/test/upgrade-e2e/post_upgrade_test.go +++ b/test/upgrade-e2e/post_upgrade_test.go @@ -63,7 +63,7 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { require.NoError(t, err) require.True(t, found) - t.Log("Checking that the ClusterCatalog is unpacked") + t.Log("Checking that the ClusterCatalog is serving") require.EventuallyWithT(t, func(ct *assert.CollectT) { var clusterCatalog catalogdv1alpha1.ClusterCatalog assert.NoError(ct, c.Get(ctx, types.NamespacedName{Name: testClusterCatalogName}, &clusterCatalog)) From 7d9f2969e149c8c85575ec78c2494cea17f4e925 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Fri, 4 Oct 2024 16:33:35 -0400 Subject: [PATCH 055/694] pre-upgrade-setup.sh: use updated ClusterCatalog API (#1347) Signed-off-by: Joe Lanford --- hack/test/pre-upgrade-setup.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hack/test/pre-upgrade-setup.sh b/hack/test/pre-upgrade-setup.sh index 8eed4c81d0..00734f9527 100755 --- a/hack/test/pre-upgrade-setup.sh +++ b/hack/test/pre-upgrade-setup.sh @@ -26,11 +26,10 @@ metadata: name: ${TEST_CLUSTER_CATALOG_NAME} spec: source: - type: image + type: Image image: ref: ${TEST_CATALOG_IMG} pollInterval: 24h - insecureSkipTLSVerify: true EOF kubectl apply -f - < Date: Fri, 4 Oct 2024 16:50:41 -0400 Subject: [PATCH 056/694] :book: add public api docs (#1331) * public api docs Signed-off-by: everettraven * post rebase fixes Signed-off-by: everettraven * address nits Signed-off-by: everettraven * Update docs/api-reference/catalogd-webserver.md Co-authored-by: Joe Lanford --------- Signed-off-by: everettraven Co-authored-by: Joe Lanford --- docs/api-reference/catalogd-webserver.md | 88 ++++++++++++++++++++++++ docs/project/public-api.md | 12 ++++ mkdocs.yml | 2 + 3 files changed, 102 insertions(+) create mode 100644 docs/api-reference/catalogd-webserver.md create mode 100644 docs/project/public-api.md diff --git a/docs/api-reference/catalogd-webserver.md b/docs/api-reference/catalogd-webserver.md new file mode 100644 index 0000000000..a95772eadc --- /dev/null +++ b/docs/api-reference/catalogd-webserver.md @@ -0,0 +1,88 @@ +# Catalogd web server + +[Catalogd](https://github.com/operator-framework/catalogd), the OLM v1 component for making catalog contents available on cluster, includes +a web server that serves catalog contents to clients via an HTTP(S) endpoint. + +The endpoint to retrieve this information is provided in the `.status.contentURL` of a `ClusterCatalog` resource. +As an example: + +```yaml + contentURL: https://catalogd-service.olmv1-system.svc/catalogs/operatorhubio/all.json +``` + +!!! note + + The value of the `.status.contentURL` field in a `ClusterCatalog` resource is an arbitrary string value and can change at any time. + While there are no guarantees on the exact value of this field, it will always be a URL that resolves successfully for clients using + it to make a request from within the cluster. + +## Interacting With the Server + +### Supported HTTP Methods + +The HTTP request methods supported by the catalogd web server are: + +- [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) +- [HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) + +### Response Format + +Responses are encoded as a [JSON Lines](https://jsonlines.org/) stream of [File-Based Catalog](https://olm.operatorframework.io/docs/reference/file-based-catalogs) (FBC) [Meta](https://olm.operatorframework.io/docs/reference/file-based-catalogs/#schema) objects delimited by newlines. + +??? example "Example JSON-encoded FBC snippet" + + ```json + { + "schema": "olm.package", + "name": "cockroachdb", + "defaultChannel": "stable-v6.x", + } + { + "schema": "olm.channel", + "name": "stable-v6.x", + "package": "cockroachdb", + "entries": [ + { + "name": "cockroachdb.v6.0.0", + "skipRange": "<6.0.0" + } + ] + } + { + "schema": "olm.bundle", + "name": "cockroachdb.v6.0.0", + "package": "cockroachdb", + "image": "quay.io/openshift-community-operators/cockroachdb@sha256:d3016b1507515fc7712f9c47fd9082baf9ccb070aaab58ed0ef6e5abdedde8ba", + "properties": [ + { + "type": "olm.package", + "value": { + "packageName": "cockroachdb", + "version": "6.0.0" + } + }, + ], + } + ``` + + Corresponding JSON lines response: + ```jsonlines + {"schema":"olm.package","name":"cockroachdb","defaultChannel":"stable-v6.x"} + {"schema":"olm.channel","name":"stable-v6.x","package":"cockroachdb","entries":[{"name":"cockroachdb.v6.0.0","skipRange":"<6.0.0"}]} + {"schema":"olm.bundle","name":"cockroachdb.v6.0.0","package":"cockroachdb","image":"quay.io/openshift-community-operators/cockroachdb@sha256:d3016b1507515fc7712f9c47fd9082baf9ccb070aaab58ed0ef6e5abdedde8ba","properties":[{"type":"olm.package","value":{"packageName":"cockroachdb","version":"6.0.0"}}]} + ``` + +### Compression Support + +The `catalogd` web server supports gzip compression of responses, which can significantly reduce associated network traffic. In order to signal that the client handles compressed responses, the client must include `Accept-Encoding: gzip` as a header in the HTTP request. + +The web server will include a `Content-Encoding: gzip` header in compressed responses. + +!!! note + + Only catalogs whose uncompressed response body would result in a response size greater than 1400 bytes will be compressed. + +### Cache Header Support + +For clients interested in caching the information returned from the `catalogd` web server, the `Last-Modified` header is set +on responses and the `If-Modified-Since` header is supported for requests. diff --git a/docs/project/public-api.md b/docs/project/public-api.md new file mode 100644 index 0000000000..fed6a7d7bb --- /dev/null +++ b/docs/project/public-api.md @@ -0,0 +1,12 @@ +# Public API +The public API of OLM v1 is as follows: + +- Kubernetes APIs. For more information on these APIs, see: + - [operator-controller API reference](./api/operator-controller-api-reference.md) + - [catalogd API reference](./api/catalogd-api-reference.md) +- `Catalogd` web server. For more information on what this includes, see the [catalogd web server documentation](./api/catalogd-webserver.md) + +!!! warning + + Only what is mentioned in the above documentation references is considered part of the public API + and follows SemVer. Anything not mentioned in the above references is prone to breaking changes. diff --git a/mkdocs.yml b/mkdocs.yml index 7680fd461f..7df6b7eba9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -25,6 +25,7 @@ nav: - Design Decisions: project/olmv1_design_decisions.md - Limitations: project/olmv1_limitations.md - Roadmap: project/olmv1_roadmap.md + - Public API: project/public-api.md - Getting Started: getting-started/olmv1_getting_started.md - Tutorials: - Add a Catalog: tutorials/add-catalog.md @@ -49,6 +50,7 @@ nav: - API Reference: - Operator Controller API reference: api-reference/operator-controller-api-reference.md - CatalogD API reference: api-reference/catalogd-api-reference.md + - CatalogD Web Server reference: api-reference/catalogd-webserver.md - Contribute: - Contributing: contribute/contributing.md - Developing OLM v1: contribute/developer.md From 02d287924a88e367cd89234c876d62991237edf3 Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Fri, 4 Oct 2024 17:25:58 -0400 Subject: [PATCH 057/694] minor updates to GoDoc+API, docs, dependent label selector removed (#1333) Signed-off-by: everettraven --- api/v1alpha1/clusterextension_types.go | 19 +-- cmd/manager/main.go | 11 +- ...peratorframework.io_clusterextensions.yaml | 10 +- .../operator-controller-api-reference.md | 2 +- docs/tutorials/add-catalog.md | 10 +- docs/tutorials/install-extension.md | 84 +++++++--- docs/tutorials/upgrade-extension.md | 148 ++++++++---------- .../clusterextension_controller_test.go | 10 +- internal/controllers/common_controller.go | 4 +- .../controllers/common_controller_test.go | 2 +- test/e2e/cluster_extension_install_test.go | 28 ++-- .../extension_developer_test.go | 2 +- test/upgrade-e2e/post_upgrade_test.go | 4 +- 13 files changed, 176 insertions(+), 158 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index b68f7f2a77..ad99e72511 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -422,15 +422,12 @@ const ( TypeChannelDeprecated = "ChannelDeprecated" TypeBundleDeprecated = "BundleDeprecated" - ReasonSuccess = "Succeeded" + ReasonSucceeded = "Succeeded" ReasonDeprecated = "Deprecated" ReasonFailed = "Failed" ReasonBlocked = "Blocked" ReasonRetrying = "Retrying" - ReasonErrorGettingClient = "ErrorGettingClient" - ReasonErrorGettingReleaseState = "ErrorGettingReleaseState" - CRDUpgradeSafetyPolicyEnabled CRDUpgradeSafetyPolicy = "Enabled" CRDUpgradeSafetyPolicyDisabled CRDUpgradeSafetyPolicy = "Disabled" ) @@ -447,11 +444,9 @@ func init() { ) // TODO(user): add Reasons from above conditionsets.ConditionReasons = append(conditionsets.ConditionReasons, - ReasonSuccess, + ReasonSucceeded, ReasonDeprecated, ReasonFailed, - ReasonErrorGettingClient, - ReasonErrorGettingReleaseState, ReasonBlocked, ReasonRetrying, ) @@ -481,8 +476,7 @@ type ClusterExtensionStatus struct { // // The global set of condition types are: // - "Installed", represents whether or not the a bundle has been installed for this ClusterExtension - // - "Resolved", represents whether or not a bundle was found that satisfies the selection criteria outlined in the spec - // - "Unpacked", represents whether or not the bundle contents have been successfully unpacked + // - "Progressing", represents whether or not the ClusterExtension is progressing towards a new state // // When the ClusterExtension is sourced from a catalog, the following conditions are also possible: // - "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types @@ -491,8 +485,11 @@ type ClusterExtensionStatus struct { // - "BundleDeprecated", represents whether or not the installed bundle is deprecated // // The current set of reasons are: - // - "Success", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when unpacking a bundle's content, resolution and installation/upgrading is successful - // - "Failed", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when an error has occurred while unpacking the contents of a bundle, during resolution or installation. + // - "Succeeded", this reason is set on the "Installed" and "Progressing" conditions when initial installation and progressing to a new state is successful + // - "Failed", this reason is set on the "Installed" condition when an error has occurred while performing the initial installation. + // - "Blocked", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that requires manual intervention for recovery + // - "Retrying", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that could be resolved on subsequent reconciliation attempts + // - "Deprecated", this reason is set on the "Deprecated", "PackageDeprecated", "ChannelDeprecated", and "BundleDeprecated" conditions to signal that the installed package has been deprecated at the particular scope // // // +patchMergeKey=type diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 947fca826c..a64ba4ac26 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -30,7 +30,6 @@ import ( "github.com/spf13/pflag" apiextensionsv1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" k8slabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/selection" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/klog/v2" @@ -56,7 +55,6 @@ import ( "github.com/operator-framework/operator-controller/internal/features" "github.com/operator-framework/operator-controller/internal/finalizers" "github.com/operator-framework/operator-controller/internal/httputil" - "github.com/operator-framework/operator-controller/internal/labels" "github.com/operator-framework/operator-controller/internal/resolve" "github.com/operator-framework/operator-controller/internal/rukpak/preflights/crdupgradesafety" "github.com/operator-framework/operator-controller/internal/rukpak/source" @@ -122,13 +120,6 @@ func main() { systemNamespace = podNamespace() } - dependentRequirement, err := k8slabels.NewRequirement(labels.OwnerKindKey, selection.In, []string{ocv1alpha1.ClusterExtensionKind}) - if err != nil { - setupLog.Error(err, "unable to create dependent label selector for cache") - os.Exit(1) - } - dependentSelector := k8slabels.NewSelector().Add(*dependentRequirement) - setupLog.Info("set up manager") mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme.Scheme, @@ -144,7 +135,7 @@ func main() { DefaultNamespaces: map[string]crcache.Config{ systemNamespace: {LabelSelector: k8slabels.Everything()}, }, - DefaultLabelSelector: dependentSelector, + DefaultLabelSelector: k8slabels.Nothing(), }, // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the diff --git a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml index f24871f004..61b81606bf 100644 --- a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml +++ b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -476,8 +476,7 @@ spec: The global set of condition types are: - "Installed", represents whether or not the a bundle has been installed for this ClusterExtension - - "Resolved", represents whether or not a bundle was found that satisfies the selection criteria outlined in the spec - - "Unpacked", represents whether or not the bundle contents have been successfully unpacked + - "Progressing", represents whether or not the ClusterExtension is progressing towards a new state When the ClusterExtension is sourced from a catalog, the following conditions are also possible: - "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types @@ -486,8 +485,11 @@ spec: - "BundleDeprecated", represents whether or not the installed bundle is deprecated The current set of reasons are: - - "Success", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when unpacking a bundle's content, resolution and installation/upgrading is successful - - "Failed", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when an error has occurred while unpacking the contents of a bundle, during resolution or installation. + - "Succeeded", this reason is set on the "Installed" and "Progressing" conditions when initial installation and progressing to a new state is successful + - "Failed", this reason is set on the "Installed" condition when an error has occurred while performing the initial installation. + - "Blocked", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that requires manual intervention for recovery + - "Retrying", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that could be resolved on subsequent reconciliation attempts + - "Deprecated", this reason is set on the "Deprecated", "PackageDeprecated", "ChannelDeprecated", and "BundleDeprecated" conditions to signal that the installed package has been deprecated at the particular scope items: description: Condition contains details for one aspect of the current state of this API Resource. diff --git a/docs/api-reference/operator-controller-api-reference.md b/docs/api-reference/operator-controller-api-reference.md index 0c5aced18a..86bd901902 100644 --- a/docs/api-reference/operator-controller-api-reference.md +++ b/docs/api-reference/operator-controller-api-reference.md @@ -192,7 +192,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `install` _[ClusterExtensionInstallStatus](#clusterextensioninstallstatus)_ | | | | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterExtension.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human readable message that further elaborates on the state of the condition

The global set of condition types are:
- "Installed", represents whether or not the a bundle has been installed for this ClusterExtension
- "Resolved", represents whether or not a bundle was found that satisfies the selection criteria outlined in the spec
- "Unpacked", represents whether or not the bundle contents have been successfully unpacked

When the ClusterExtension is sourced from a catalog, the following conditions are also possible:
- "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types
- "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated
- "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated
- "BundleDeprecated", represents whether or not the installed bundle is deprecated

The current set of reasons are:
- "Success", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when unpacking a bundle's content, resolution and installation/upgrading is successful
- "Failed", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when an error has occurred while unpacking the contents of a bundle, during resolution or installation. | | | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterExtension.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human readable message that further elaborates on the state of the condition

The global set of condition types are:
- "Installed", represents whether or not the a bundle has been installed for this ClusterExtension
- "Progressing", represents whether or not the ClusterExtension is progressing towards a new state

When the ClusterExtension is sourced from a catalog, the following conditions are also possible:
- "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types
- "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated
- "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated
- "BundleDeprecated", represents whether or not the installed bundle is deprecated

The current set of reasons are:
- "Succeeded", this reason is set on the "Installed" and "Progressing" conditions when initial installation and progressing to a new state is successful
- "Failed", this reason is set on the "Installed" condition when an error has occurred while performing the initial installation.
- "Blocked", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that requires manual intervention for recovery
- "Retrying", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that could be resolved on subsequent reconciliation attempts
- "Deprecated", this reason is set on the "Deprecated", "PackageDeprecated", "ChannelDeprecated", and "BundleDeprecated" conditions to signal that the installed package has been deprecated at the particular scope | | | #### PreflightConfig diff --git a/docs/tutorials/add-catalog.md b/docs/tutorials/add-catalog.md index 60466ed23e..ad3ae06bbd 100644 --- a/docs/tutorials/add-catalog.md +++ b/docs/tutorials/add-catalog.md @@ -99,12 +99,12 @@ This catalog is distributed as an image [quay.io/operatorhubio/catalog](https:// API Version: olm.operatorframework.io/v1alpha1 Kind: ClusterCatalog Metadata: - Creation Timestamp: 2024-03-12T19:34:50Z - Finalizers: + Creation Timestamp: 2024-10-02T19:51:24Z + Finalizers: olm.operatorframework.io/delete-server-cache - Generation: 2 - Resource Version: 6469 - UID: 2e2778cb-dda6-4645-96b7-992e8dd37503 + Generation: 1 + Resource Version: 33321 + UID: 52894532-0646-41a5-8285-c7f48bba49e4 Spec: Priority: 0 Source: diff --git a/docs/tutorials/install-extension.md b/docs/tutorials/install-extension.md index 4841bb5180..95bdb5c3ab 100644 --- a/docs/tutorials/install-extension.md +++ b/docs/tutorials/install-extension.md @@ -103,35 +103,73 @@ For information on determining the ServiceAccount's permission, please see [Deri ??? success ``` text title="Example output" - Name: my-camel-k + Name: argocd Namespace: Labels: Annotations: API Version: olm.operatorframework.io/v1alpha1 Kind: ClusterExtension Metadata: - Creation Timestamp: 2024-03-15T15:03:47Z - Generation: 1 - Resource Version: 7691 - UID: d756879f-217d-4ebe-85b1-8427bbb2f1df + Creation Timestamp: 2024-10-03T16:02:40Z + Finalizers: + olm.operatorframework.io/cleanup-unpack-cache + olm.operatorframework.io/cleanup-contentmanager-cache + Generation: 2 + Resource Version: 1174 + UID: 0fcaf3f5-d142-4c7e-8d88-c88a549f7764 Spec: - Package Name: camel-k - Upgrade Constraint Policy: Enforce + Install: + Namespace: argocd + Service Account: + Name: argocd-installer + Source: + Catalog: + Package Name: argocd-operator + Selector: + Upgrade Constraint Policy: CatalogProvided + Version: 0.6.0 + Source Type: Catalog Status: - Conditions: - Last Transition Time: 2024-03-15T15:03:50Z - Message: resolved to "quay.io/operatorhubio/camel-k@sha256:d2b74c43ec8f9294450c9dcf2057be328d0998bb924ad036db489af79d1b39c3" - Observed Generation: 1 - Reason: Success - Status: True - Type: Resolved - Last Transition Time: 2024-03-15T15:04:13Z - Message: installed from "quay.io/operatorhubio/camel-k@sha256:d2b74c43ec8f9294450c9dcf2057be328d0998bb924ad036db489af79d1b39c3" - Observed Generation: 1 - Reason: Success - Status: True - Type: Installed - Installed Bundle Resource: quay.io/operatorhubio/camel-k@sha256:d2b74c43ec8f9294450c9dcf2057be328d0998bb924ad036db489af79d1b39c3 - Resolved Bundle Resource: quay.io/operatorhubio/camel-k@sha256:d2b74c43ec8f9294450c9dcf2057be328d0998bb924ad036db489af79d1b39c3 - Events: + Conditions: + Last Transition Time: 2024-10-03T16:02:41Z + Message: + Observed Generation: 2 + Reason: Deprecated + Status: False + Type: Deprecated + Last Transition Time: 2024-10-03T16:02:41Z + Message: + Observed Generation: 2 + Reason: Deprecated + Status: False + Type: PackageDeprecated + Last Transition Time: 2024-10-03T16:02:41Z + Message: + Observed Generation: 2 + Reason: Deprecated + Status: False + Type: ChannelDeprecated + Last Transition Time: 2024-10-03T16:02:41Z + Message: + Observed Generation: 2 + Reason: Deprecated + Status: False + Type: BundleDeprecated + Last Transition Time: 2024-10-03T16:02:43Z + Message: Installed bundle quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3 successfully + Observed Generation: 2 + Reason: Succeeded + Status: True + Type: Installed + Last Transition Time: 2024-10-03T16:02:43Z + Message: desired state reached + Observed Generation: 2 + Reason: Succeeded + Status: False + Type: Progressing + Install: + Bundle: + Name: argocd-operator.v0.6.0 + Version: 0.6.0 + Events: ``` diff --git a/docs/tutorials/upgrade-extension.md b/docs/tutorials/upgrade-extension.md index e55a53f963..ea0a203441 100644 --- a/docs/tutorials/upgrade-extension.md +++ b/docs/tutorials/upgrade-extension.md @@ -92,83 +92,73 @@ spec: ??? success ``` text title="Example output" - Name: argocd - Namespace: - Labels: olm.operatorframework.io/owner-kind=ClusterExtension - olm.operatorframework.io/owner-name=argocd - Annotations: - API Version: olm.operatorframework.io/v1alpha1 - Kind: ClusterExtension - Metadata: - Creation Timestamp: 2024-09-06T13:38:38Z - Finalizers: - olm.operatorframework.io/cleanup-unpack-cache - olm.operatorframework.io/cleanup-contentmanager-cache - Generation: 5 - Resource Version: 21167 - UID: 5abdf57d-aedc-45d4-ba0d-a86e785fd34a - Spec: - Install: - Namespace: argocd - Service Account: - Name: argocd-installer - Source: - Catalog: - Package Name: argocd-operator - Selector: - Upgrade Constraint Policy: Enforce - Version: 0.6.0 - Source Type: Catalog - Status: - Conditions: - Last Transition Time: 2024-09-06T13:38:38Z - Message: - Observed Generation: 5 - Reason: Deprecated - Status: False - Type: Deprecated - Last Transition Time: 2024-09-06T13:38:38Z - Message: - Observed Generation: 5 - Reason: Deprecated - Status: False - Type: PackageDeprecated - Last Transition Time: 2024-09-06T13:38:38Z - Message: - Observed Generation: 5 - Reason: Deprecated - Status: False - Type: ChannelDeprecated - Last Transition Time: 2024-09-06T13:38:38Z - Message: - Observed Generation: 5 - Reason: Deprecated - Status: False - Type: BundleDeprecated - Last Transition Time: 2024-09-06T13:40:14Z - Message: resolved to "quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3" - Observed Generation: 5 - Reason: Success - Status: True - Type: Resolved - Last Transition Time: 2024-09-06T13:38:38Z - Message: unpack successful: - Observed Generation: 5 - Reason: UnpackSuccess - Status: True - Type: Unpacked - Last Transition Time: 2024-09-06T13:40:31Z - Message: Installed bundle quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3 successfully - Observed Generation: 5 - Reason: Success - Status: True - Type: Installed - Install: - Bundle: - Name: argocd-operator.v0.6.0 - Version: 0.6.0 - Resolution: - Bundle: - Name: argocd-operator.v0.6.0 - Version: 0.6.0 + apiVersion: olm.operatorframework.io/v1alpha1 + kind: ClusterExtension + metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"olm.operatorframework.io/v1alpha1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"argocd"},"spec":{"install":{"namespace":"argocd","serviceAccount":{"name":"argocd-installer"}},"source":{"catalog":{"packageName":"argocd-operator","version":"0.6.0"},"sourceType":"Catalog"}}} + creationTimestamp: "2024-10-03T16:02:40Z" + finalizers: + - olm.operatorframework.io/cleanup-unpack-cache + - olm.operatorframework.io/cleanup-contentmanager-cache + generation: 2 + name: argocd + resourceVersion: "1174" + uid: 0fcaf3f5-d142-4c7e-8d88-c88a549f7764 + spec: + install: + namespace: argocd + serviceAccount: + name: argocd-installer + source: + catalog: + packageName: argocd-operator + selector: {} + upgradeConstraintPolicy: CatalogProvided + version: 0.6.0 + sourceType: Catalog + status: + conditions: + - lastTransitionTime: "2024-10-03T16:02:41Z" + message: "" + observedGeneration: 2 + reason: Deprecated + status: "False" + type: Deprecated + - lastTransitionTime: "2024-10-03T16:02:41Z" + message: "" + observedGeneration: 2 + reason: Deprecated + status: "False" + type: PackageDeprecated + - lastTransitionTime: "2024-10-03T16:02:41Z" + message: "" + observedGeneration: 2 + reason: Deprecated + status: "False" + type: ChannelDeprecated + - lastTransitionTime: "2024-10-03T16:02:41Z" + message: "" + observedGeneration: 2 + reason: Deprecated + status: "False" + type: BundleDeprecated + - lastTransitionTime: "2024-10-03T16:02:43Z" + message: Installed bundle quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3 + successfully + observedGeneration: 2 + reason: Succeeded + status: "True" + type: Installed + - lastTransitionTime: "2024-10-03T16:02:43Z" + message: desired state reached + observedGeneration: 2 + reason: Succeeded + status: "False" + type: Progressing + install: + bundle: + name: argocd-operator.v0.6.0 + version: 0.6.0 ``` diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index 23f81363cb..8f7331384e 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -421,7 +421,7 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) require.Equal(t, metav1.ConditionTrue, installedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) + require.Equal(t, ocv1alpha1.ReasonSucceeded, installedCond.Reason) t.Log("By checking the expected progressing conditions") progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) @@ -505,7 +505,7 @@ func TestClusterExtensionManagerFailed(t *testing.T) { installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) require.Equal(t, metav1.ConditionTrue, installedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) + require.Equal(t, ocv1alpha1.ReasonSucceeded, installedCond.Reason) t.Log("By checking the expected progressing conditions") progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) @@ -591,7 +591,7 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) require.Equal(t, metav1.ConditionTrue, installedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) + require.Equal(t, ocv1alpha1.ReasonSucceeded, installedCond.Reason) t.Log("By checking the expected progressing conditions") progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) @@ -674,13 +674,13 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) require.NotNil(t, installedCond) require.Equal(t, metav1.ConditionTrue, installedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, installedCond.Reason) + require.Equal(t, ocv1alpha1.ReasonSucceeded, installedCond.Reason) t.Log("By checking the expected progressing conditions") progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) require.NotNil(t, progressingCond) require.Equal(t, metav1.ConditionFalse, progressingCond.Status) - require.Equal(t, ocv1alpha1.ReasonSuccess, progressingCond.Reason) + require.Equal(t, ocv1alpha1.ReasonSucceeded, progressingCond.Reason) require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) } diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index 8210f39997..d04cd0a01f 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -31,7 +31,7 @@ func setInstalledStatusConditionSuccess(ext *ocv1alpha1.ClusterExtension, messag apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ Type: ocv1alpha1.TypeInstalled, Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonSuccess, + Reason: ocv1alpha1.ReasonSucceeded, Message: message, ObservedGeneration: ext.GetGeneration(), }) @@ -56,7 +56,7 @@ func setStatusProgressing(ext *ocv1alpha1.ClusterExtension, err error) { progressingCond := metav1.Condition{ Type: ocv1alpha1.TypeProgressing, Status: metav1.ConditionFalse, - Reason: ocv1alpha1.ReasonSuccess, + Reason: ocv1alpha1.ReasonSucceeded, Message: "desired state reached", ObservedGeneration: ext.GetGeneration(), } diff --git a/internal/controllers/common_controller_test.go b/internal/controllers/common_controller_test.go index 8715464d63..8f703fc6ef 100644 --- a/internal/controllers/common_controller_test.go +++ b/internal/controllers/common_controller_test.go @@ -28,7 +28,7 @@ func TestSetStatusProgressing(t *testing.T) { expected: metav1.Condition{ Type: ocv1alpha1.TypeProgressing, Status: metav1.ConditionFalse, - Reason: ocv1alpha1.ReasonSuccess, + Reason: ocv1alpha1.ReasonSucceeded, Message: "desired state reached", }, }, diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 18ade96771..ed514a70bf 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -276,7 +276,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -286,7 +286,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle) } @@ -377,7 +377,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -433,7 +433,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -449,7 +449,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } @@ -484,7 +484,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -499,7 +499,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } @@ -544,7 +544,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -568,7 +568,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } @@ -625,7 +625,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -649,7 +649,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } @@ -688,7 +688,7 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") } }, pollDuration, pollInterval) @@ -786,7 +786,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") assert.NotEmpty(ct, clusterExtension.Status.Install) } @@ -798,7 +798,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } diff --git a/test/extension-developer-e2e/extension_developer_test.go b/test/extension-developer-e2e/extension_developer_test.go index 2db90b674a..4a11a75300 100644 --- a/test/extension-developer-e2e/extension_developer_test.go +++ b/test/extension-developer-e2e/extension_developer_test.go @@ -209,7 +209,7 @@ func TestExtensionDeveloper(t *testing.T) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) }, 2*time.Minute, time.Second) require.NoError(t, c.Delete(context.Background(), catalog)) require.NoError(t, c.Delete(context.Background(), clusterExtension)) diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go index f893b8e2a8..78f7284a8c 100644 --- a/test/upgrade-e2e/post_upgrade_test.go +++ b/test/upgrade-e2e/post_upgrade_test.go @@ -84,7 +84,7 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") if assert.NotNil(ct, clusterExtension.Status.Install) { assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle.Version) @@ -105,7 +105,7 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { if !assert.NotNil(ct, cond) { return } - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") assert.Equal(ct, ocv1alpha1.BundleMetadata{Name: "prometheus-operator.1.0.1", Version: "1.0.1"}, clusterExtension.Status.Install.Bundle) assert.NotEqual(ct, previousVersion, clusterExtension.Status.Install.Bundle.Version) From eb17fc987598fbce9298c814e23da171246e33c1 Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Mon, 7 Oct 2024 15:23:45 +0200 Subject: [PATCH 058/694] :book: fix public api links (#1348) Signed-off-by: Per Goncalves da Silva Co-authored-by: Per Goncalves da Silva --- docs/project/public-api.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/project/public-api.md b/docs/project/public-api.md index fed6a7d7bb..a0e45ce93d 100644 --- a/docs/project/public-api.md +++ b/docs/project/public-api.md @@ -2,9 +2,9 @@ The public API of OLM v1 is as follows: - Kubernetes APIs. For more information on these APIs, see: - - [operator-controller API reference](./api/operator-controller-api-reference.md) - - [catalogd API reference](./api/catalogd-api-reference.md) -- `Catalogd` web server. For more information on what this includes, see the [catalogd web server documentation](./api/catalogd-webserver.md) + - [operator-controller API reference](../api-reference/operator-controller-api-reference.md) + - [catalogd API reference](../api-reference/catalogd-api-reference.md) +- `Catalogd` web server. For more information on what this includes, see the [catalogd web server documentation](../api-reference/catalogd-webserver.md) !!! warning From 5097435c5aa5d0d62c723a0d4d7b95c284c45540 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:25:15 +0000 Subject: [PATCH 059/694] :seedling: Bump pymdown-extensions from 10.11.1 to 10.11.2 (#1326) Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.11.1 to 10.11.2. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.11.1...10.11.2) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 350064cabc..f36f0bf371 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 Pygments==2.18.0 -pymdown-extensions==10.11.1 +pymdown-extensions==10.11.2 pyquery==2.0.1 python-dateutil==2.9.0.post0 PyYAML==6.0.2 From dd1730a8dcbe9890552e225b94ff8e40a40f74d4 Mon Sep 17 00:00:00 2001 From: Lalatendu Mohanty Date: Tue, 8 Oct 2024 03:42:57 -0400 Subject: [PATCH 060/694] Adding the docs website to the readme (#1350) Signed-off-by: Lalatendu Mohanty --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 298d7098d1..707f654c6e 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,13 @@ OLM v1 consists of two different components: For a more complete overview of OLM v1 and how it differs from OLM v0, see our [overview](docs/project/olmv1_design_decisions.md). +## Documentation + +The documentation currently lives at [website](https://operator-framework.github.io/operator-controller/). The source of the documentation exists in this repository, see [docs directory](docs/). + ## Getting Started -To get started with OLM v1, please see our [Getting Started](docs/getting-started/olmv1_getting_started.md) documentation. +To get started with OLM v1, please see our [Getting Started](https://operator-framework.github.io/operator-controller/getting-started/olmv1_getting_started/) documentation. ## License From f35edf63b8e75bb06d05d1b50eb8e02fba30ba3c Mon Sep 17 00:00:00 2001 From: Anik Date: Tue, 8 Oct 2024 19:56:56 +0530 Subject: [PATCH 061/694] =?UTF-8?q?=E2=9C=A8=20Add=20PullSecret=20controll?= =?UTF-8?q?er=20to=20save=20pull=20secret=20data=20locally=20(#1322)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Add PullSecret controller to save pull secret data locally RFC: https://docs.google.com/document/d/1BXD6kj5zXHcGiqvJOikU2xs8kV26TPnzEKp6n7TKD4M/edit#heading=h.x3tfh25grvnv * main.go: improved cache configuration for watching pull secret Signed-off-by: Joe Lanford --------- Signed-off-by: Joe Lanford --- cmd/manager/main.go | 101 +++++++++++----- .../controllers/pull_secret_controller.go | 110 ++++++++++++++++++ .../pull_secret_controller_test.go | 98 ++++++++++++++++ internal/rukpak/source/containers_image.go | 22 ++-- .../rukpak/source/containers_image_test.go | 87 +++++++------- 5 files changed, 340 insertions(+), 78 deletions(-) create mode 100644 internal/controllers/pull_secret_controller.go create mode 100644 internal/controllers/pull_secret_controller_test.go diff --git a/cmd/manager/main.go b/cmd/manager/main.go index a64ba4ac26..b03472dfc0 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -23,13 +23,18 @@ import ( "net/http" "os" "path/filepath" + "strings" "time" "github.com/containers/image/v5/types" "github.com/go-logr/logr" "github.com/spf13/pflag" + corev1 "k8s.io/api/core/v1" apiextensionsv1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" + "k8s.io/apimachinery/pkg/fields" k8slabels "k8s.io/apimachinery/pkg/labels" + k8stypes "k8s.io/apimachinery/pkg/types" + apimachineryrand "k8s.io/apimachinery/pkg/util/rand" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/klog/v2" @@ -67,7 +72,7 @@ var ( defaultSystemNamespace = "olmv1-system" ) -const authFilePath = "/etc/operator-controller/auth.json" +const authFilePrefix = "operator-controller-global-pull-secrets" // podNamespace checks whether the controller is running in a Pod vs. // being run locally by inspecting the namespace file that gets mounted @@ -90,6 +95,7 @@ func main() { operatorControllerVersion bool systemNamespace string caCertDir string + globalPullSecret string ) flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") @@ -100,6 +106,7 @@ func main() { flag.StringVar(&cachePath, "cache-path", "/var/cache", "The local directory path used for filesystem based caching") flag.BoolVar(&operatorControllerVersion, "version", false, "Prints operator-controller version information") flag.StringVar(&systemNamespace, "system-namespace", "", "Configures the namespace that gets used to deploy system resources.") + flag.StringVar(&globalPullSecret, "global-pull-secret", "", "The / of the global pull secret that is going to be used to pull bundle images.") klog.InitFlags(flag.CommandLine) @@ -116,27 +123,51 @@ func main() { setupLog.Info("starting up the controller", "version info", version.String()) + authFilePath := filepath.Join(os.TempDir(), fmt.Sprintf("%s-%s.json", authFilePrefix, apimachineryrand.String(8))) + var globalPullSecretKey *k8stypes.NamespacedName + if globalPullSecret != "" { + secretParts := strings.Split(globalPullSecret, "/") + if len(secretParts) != 2 { + setupLog.Error(fmt.Errorf("incorrect number of components"), "value of global-pull-secret should be of the format /") + os.Exit(1) + } + globalPullSecretKey = &k8stypes.NamespacedName{Name: secretParts[1], Namespace: secretParts[0]} + } + if systemNamespace == "" { systemNamespace = podNamespace() } setupLog.Info("set up manager") + cacheOptions := crcache.Options{ + ByObject: map[client.Object]crcache.ByObject{ + &ocv1alpha1.ClusterExtension{}: {Label: k8slabels.Everything()}, + &catalogd.ClusterCatalog{}: {Label: k8slabels.Everything()}, + }, + DefaultNamespaces: map[string]crcache.Config{ + systemNamespace: {LabelSelector: k8slabels.Everything()}, + }, + DefaultLabelSelector: k8slabels.Nothing(), + } + if globalPullSecretKey != nil { + cacheOptions.ByObject[&corev1.Secret{}] = crcache.ByObject{ + Namespaces: map[string]crcache.Config{ + globalPullSecretKey.Namespace: { + LabelSelector: k8slabels.Everything(), + FieldSelector: fields.SelectorFromSet(map[string]string{ + "metadata.name": globalPullSecretKey.Name, + }), + }, + }, + } + } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme.Scheme, Metrics: server.Options{BindAddress: metricsAddr}, HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "9c4404e7.operatorframework.io", - Cache: crcache.Options{ - ByObject: map[client.Object]crcache.ByObject{ - &ocv1alpha1.ClusterExtension{}: {Label: k8slabels.Everything()}, - &catalogd.ClusterCatalog{}: {Label: k8slabels.Everything()}, - }, - DefaultNamespaces: map[string]crcache.Config{ - systemNamespace: {LabelSelector: k8slabels.Everything()}, - }, - DefaultLabelSelector: k8slabels.Nothing(), - }, + Cache: cacheOptions, // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly @@ -191,12 +222,21 @@ func main() { unpacker := &source.ContainersImageRegistry{ BaseCachePath: filepath.Join(cachePath, "unpack"), - SourceContext: &types.SystemContext{ - DockerCertPath: caCertDir, - OCICertPath: caCertDir, - AuthFilePath: authFilePathIfPresent(setupLog), - }, - } + SourceContextFunc: func(logger logr.Logger) (*types.SystemContext, error) { + srcContext := &types.SystemContext{ + DockerCertPath: caCertDir, + OCICertPath: caCertDir, + } + if _, err := os.Stat(authFilePath); err == nil && globalPullSecretKey != nil { + logger.Info("using available authentication information for pulling image") + srcContext.AuthFilePath = authFilePath + } else if os.IsNotExist(err) { + logger.Info("no authentication information found for pulling image, proceeding without auth") + } else { + return nil, fmt.Errorf("could not stat auth file, error: %w", err) + } + return srcContext, nil + }} clusterExtensionFinalizers := crfinalizer.NewFinalizers() if err := clusterExtensionFinalizers.Register(controllers.ClusterExtensionCleanupUnpackCacheFinalizer, finalizers.FinalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { @@ -281,6 +321,19 @@ func main() { os.Exit(1) } + if globalPullSecretKey != nil { + setupLog.Info("creating SecretSyncer controller for watching secret", "Secret", globalPullSecret) + err := (&controllers.PullSecretReconciler{ + Client: mgr.GetClient(), + AuthFilePath: authFilePath, + SecretKey: *globalPullSecretKey, + }).SetupWithManager(mgr) + if err != nil { + setupLog.Error(err, "unable to create controller", "controller", "SecretSyncer") + os.Exit(1) + } + } + //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { @@ -298,18 +351,8 @@ func main() { setupLog.Error(err, "problem running manager") os.Exit(1) } -} - -func authFilePathIfPresent(logger logr.Logger) string { - _, err := os.Stat(authFilePath) - if os.IsNotExist(err) { - logger.Info("auth file not found, skipping configuration of global auth file", "path", authFilePath) - return "" - } - if err != nil { - logger.Error(err, "unable to access auth file path", "path", authFilePath) + if err := os.Remove(authFilePath); err != nil { + setupLog.Error(err, "failed to cleanup temporary auth file") os.Exit(1) } - logger.Info("auth file found, configuring globally for image registry interactions", "path", authFilePath) - return authFilePath } diff --git a/internal/controllers/pull_secret_controller.go b/internal/controllers/pull_secret_controller.go new file mode 100644 index 0000000000..6db1ae564e --- /dev/null +++ b/internal/controllers/pull_secret_controller.go @@ -0,0 +1,110 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "fmt" + "os" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +// PullSecretReconciler reconciles a specific Secret object +// that contains global pull secrets for pulling bundle images +type PullSecretReconciler struct { + client.Client + SecretKey types.NamespacedName + AuthFilePath string +} + +func (r *PullSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + if req.Name != r.SecretKey.Name || req.Namespace != r.SecretKey.Namespace { + logger.Error(fmt.Errorf("received unexpected request for Secret %v/%v", req.Namespace, req.Name), "reconciliation error") + return ctrl.Result{}, nil + } + + secret := &corev1.Secret{} + err := r.Get(ctx, req.NamespacedName, secret) + if err != nil { + if apierrors.IsNotFound(err) { + logger.Info("secret not found") + return r.deleteSecretFile(logger) + } + logger.Error(err, "failed to get Secret") + return ctrl.Result{}, err + } + + return r.writeSecretToFile(logger, secret) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *PullSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { + _, err := ctrl.NewControllerManagedBy(mgr). + For(&corev1.Secret{}). + WithEventFilter(newSecretPredicate(r.SecretKey)). + Build(r) + + return err +} + +func newSecretPredicate(key types.NamespacedName) predicate.Predicate { + return predicate.NewPredicateFuncs(func(obj client.Object) bool { + return obj.GetName() == key.Name && obj.GetNamespace() == key.Namespace + }) +} + +// writeSecretToFile writes the secret data to the specified file +func (r *PullSecretReconciler) writeSecretToFile(logger logr.Logger, secret *corev1.Secret) (ctrl.Result, error) { + // image registry secrets are always stored with the key .dockerconfigjson + // ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials + dockerConfigJSON, ok := secret.Data[".dockerconfigjson"] + if !ok { + logger.Error(fmt.Errorf("expected secret.Data key not found"), "expected secret Data to contain key .dockerconfigjson") + return ctrl.Result{}, nil + } + // expected format for auth.json + // https://github.com/containers/image/blob/main/docs/containers-auth.json.5.md + err := os.WriteFile(r.AuthFilePath, dockerConfigJSON, 0600) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to write secret data to file: %w", err) + } + logger.Info("saved global pull secret data locally") + return ctrl.Result{}, nil +} + +// deleteSecretFile deletes the auth file if the secret is deleted +func (r *PullSecretReconciler) deleteSecretFile(logger logr.Logger) (ctrl.Result, error) { + logger.Info("deleting local auth file", "file", r.AuthFilePath) + if err := os.Remove(r.AuthFilePath); err != nil { + if os.IsNotExist(err) { + logger.Info("auth file does not exist, nothing to delete") + return ctrl.Result{}, nil + } + return ctrl.Result{}, fmt.Errorf("failed to delete secret file: %w", err) + } + logger.Info("auth file deleted successfully") + return ctrl.Result{}, nil +} diff --git a/internal/controllers/pull_secret_controller_test.go b/internal/controllers/pull_secret_controller_test.go new file mode 100644 index 0000000000..dc240e7629 --- /dev/null +++ b/internal/controllers/pull_secret_controller_test.go @@ -0,0 +1,98 @@ +package controllers_test + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/operator-framework/operator-controller/internal/controllers" + "github.com/operator-framework/operator-controller/internal/scheme" +) + +func TestSecretSyncerReconciler(t *testing.T) { + secretData := []byte(`{"auths":{"exampleRegistry": "exampledata"}}`) + authFileName := "test-auth.json" + for _, tt := range []struct { + name string + secret *corev1.Secret + addSecret bool + wantErr string + fileShouldExistBefore bool + fileShouldExistAfter bool + }{ + { + name: "secret exists, content gets saved to authFile", + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-secret", + Namespace: "test-secret-namespace", + }, + Data: map[string][]byte{ + ".dockerconfigjson": secretData, + }, + }, + addSecret: true, + fileShouldExistBefore: false, + fileShouldExistAfter: true, + }, + { + name: "secret does not exist, file exists previously, file should get deleted", + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-secret", + Namespace: "test-secret-namespace", + }, + Data: map[string][]byte{ + ".dockerconfigjson": secretData, + }, + }, + addSecret: false, + fileShouldExistBefore: true, + fileShouldExistAfter: false, + }, + } { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + tempAuthFile := filepath.Join(t.TempDir(), authFileName) + clientBuilder := fake.NewClientBuilder().WithScheme(scheme.Scheme) + if tt.addSecret { + clientBuilder = clientBuilder.WithObjects(tt.secret) + } + cl := clientBuilder.Build() + + secretKey := types.NamespacedName{Namespace: tt.secret.Namespace, Name: tt.secret.Name} + r := &controllers.PullSecretReconciler{ + Client: cl, + SecretKey: secretKey, + AuthFilePath: tempAuthFile, + } + if tt.fileShouldExistBefore { + err := os.WriteFile(tempAuthFile, secretData, 0600) + require.NoError(t, err) + } + res, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: secretKey}) + if tt.wantErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.wantErr) + } + require.Equal(t, ctrl.Result{}, res) + + if tt.fileShouldExistAfter { + _, err := os.Stat(tempAuthFile) + require.NoError(t, err) + } else { + _, err := os.Stat(tempAuthFile) + require.True(t, os.IsNotExist(err)) + } + }) + } +} diff --git a/internal/rukpak/source/containers_image.go b/internal/rukpak/source/containers_image.go index 2248e64774..132f6fcd3b 100644 --- a/internal/rukpak/source/containers_image.go +++ b/internal/rukpak/source/containers_image.go @@ -26,8 +26,8 @@ import ( ) type ContainersImageRegistry struct { - BaseCachePath string - SourceContext *types.SystemContext + BaseCachePath string + SourceContextFunc func(logger logr.Logger) (*types.SystemContext, error) } func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSource) (*Result, error) { @@ -41,12 +41,16 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour return nil, reconcile.TerminalError(fmt.Errorf("error parsing bundle, bundle %s has a nil image source", bundle.Name)) } + srcCtx, err := i.SourceContextFunc(l) + if err != nil { + return nil, err + } ////////////////////////////////////////////////////// // // Resolve a canonical reference for the image. // ////////////////////////////////////////////////////// - imgRef, canonicalRef, _, err := resolveReferences(ctx, bundle.Image.Ref, i.SourceContext) + imgRef, canonicalRef, _, err := resolveReferences(ctx, bundle.Image.Ref, srcCtx) if err != nil { return nil, err } @@ -102,7 +106,7 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour // a policy context for the image pull. // ////////////////////////////////////////////////////// - policyContext, err := loadPolicyContext(i.SourceContext, l) + policyContext, err := loadPolicyContext(srcCtx, l) if err != nil { return nil, fmt.Errorf("error loading policy context: %w", err) } @@ -118,7 +122,7 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour // ////////////////////////////////////////////////////// if _, err := copy.Image(ctx, policyContext, layoutRef, dockerRef, ©.Options{ - SourceCtx: i.SourceContext, + SourceCtx: srcCtx, }); err != nil { return nil, fmt.Errorf("error copying image: %w", err) } @@ -129,7 +133,7 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour // Mount the image we just pulled // ////////////////////////////////////////////////////// - if err := i.unpackImage(ctx, unpackPath, layoutRef); err != nil { + if err := i.unpackImage(ctx, unpackPath, layoutRef, srcCtx); err != nil { if cleanupErr := deleteRecursive(unpackPath); cleanupErr != nil { err = errors.Join(err, cleanupErr) } @@ -225,8 +229,8 @@ func loadPolicyContext(sourceContext *types.SystemContext, l logr.Logger) (*sign return signature.NewPolicyContext(policy) } -func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath string, imageReference types.ImageReference) error { - img, err := imageReference.NewImage(ctx, i.SourceContext) +func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath string, imageReference types.ImageReference, sourceContext *types.SystemContext) error { + img, err := imageReference.NewImage(ctx, sourceContext) if err != nil { return fmt.Errorf("error reading image: %w", err) } @@ -236,7 +240,7 @@ func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath st } }() - layoutSrc, err := imageReference.NewImageSource(ctx, i.SourceContext) + layoutSrc, err := imageReference.NewImageSource(ctx, sourceContext) if err != nil { return fmt.Errorf("error creating image source: %w", err) } diff --git a/internal/rukpak/source/containers_image_test.go b/internal/rukpak/source/containers_image_test.go index 4d793a000d..ea7a698329 100644 --- a/internal/rukpak/source/containers_image_test.go +++ b/internal/rukpak/source/containers_image_test.go @@ -14,6 +14,7 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/types" + "github.com/go-logr/logr" "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/registry" "github.com/opencontainers/go-digest" @@ -34,8 +35,8 @@ func TestUnpackValidInsecure(t *testing.T) { defer cleanup() unpacker := &source.ContainersImageRegistry{ - BaseCachePath: t.TempDir(), - SourceContext: buildPullContext(t, imageTagRef), + BaseCachePath: t.TempDir(), + SourceContextFunc: buildPullContextfunc(t, imageTagRef), } bundleSource := &source.BundleSource{ Name: "test-bundle", @@ -69,8 +70,8 @@ func TestUnpackValidUsesCache(t *testing.T) { defer cleanup() unpacker := &source.ContainersImageRegistry{ - BaseCachePath: t.TempDir(), - SourceContext: buildPullContext(t, imageDigestRef), + BaseCachePath: t.TempDir(), + SourceContextFunc: buildPullContextfunc(t, imageDigestRef), } bundleSource := &source.BundleSource{ @@ -102,8 +103,8 @@ func TestUnpackCacheCheckError(t *testing.T) { defer cleanup() unpacker := &source.ContainersImageRegistry{ - BaseCachePath: t.TempDir(), - SourceContext: buildPullContext(t, imageTagRef), + BaseCachePath: t.TempDir(), + SourceContextFunc: buildPullContextfunc(t, imageTagRef), } bundleSource := &source.BundleSource{ Name: "test-bundle", @@ -131,8 +132,8 @@ func TestUnpackNameOnlyImageReference(t *testing.T) { defer cleanup() unpacker := &source.ContainersImageRegistry{ - BaseCachePath: t.TempDir(), - SourceContext: buildPullContext(t, imageTagRef), + BaseCachePath: t.TempDir(), + SourceContextFunc: buildPullContextfunc(t, imageTagRef), } bundleSource := &source.BundleSource{ Name: "test-bundle", @@ -153,8 +154,8 @@ func TestUnpackUnservedTaggedImageReference(t *testing.T) { defer cleanup() unpacker := &source.ContainersImageRegistry{ - BaseCachePath: t.TempDir(), - SourceContext: buildPullContext(t, imageTagRef), + BaseCachePath: t.TempDir(), + SourceContextFunc: buildPullContextfunc(t, imageTagRef), } bundleSource := &source.BundleSource{ Name: "test-bundle", @@ -175,8 +176,8 @@ func TestUnpackUnservedCanonicalImageReference(t *testing.T) { defer cleanup() unpacker := &source.ContainersImageRegistry{ - BaseCachePath: t.TempDir(), - SourceContext: buildPullContext(t, imageTagRef), + BaseCachePath: t.TempDir(), + SourceContextFunc: buildPullContextfunc(t, imageTagRef), } origRef := imageDigestRef.String() @@ -232,7 +233,11 @@ func TestUnpackInvalidNilImage(t *testing.T) { } func TestUnpackInvalidImageRef(t *testing.T) { - unpacker := &source.ContainersImageRegistry{} + unpacker := &source.ContainersImageRegistry{ + SourceContextFunc: func(logr.Logger) (*types.SystemContext, error) { + return &types.SystemContext{}, nil + }, + } // Create BundleSource with malformed image reference bundleSource := &source.BundleSource{ Name: "test-bundle", @@ -255,8 +260,8 @@ func TestUnpackUnexpectedFile(t *testing.T) { defer cleanup() unpacker := &source.ContainersImageRegistry{ - BaseCachePath: t.TempDir(), - SourceContext: buildPullContext(t, imageTagRef), + BaseCachePath: t.TempDir(), + SourceContextFunc: buildPullContextfunc(t, imageTagRef), } bundleSource := &source.BundleSource{ Name: "test-bundle", @@ -280,8 +285,8 @@ func TestUnpackCopySucceedsMountFails(t *testing.T) { defer cleanup() unpacker := &source.ContainersImageRegistry{ - BaseCachePath: t.TempDir(), - SourceContext: buildPullContext(t, imageTagRef), + BaseCachePath: t.TempDir(), + SourceContextFunc: buildPullContextfunc(t, imageTagRef), } bundleSource := &source.BundleSource{ Name: "test-bundle", @@ -305,8 +310,8 @@ func TestCleanup(t *testing.T) { defer cleanup() unpacker := &source.ContainersImageRegistry{ - BaseCachePath: t.TempDir(), - SourceContext: buildPullContext(t, imageTagRef), + BaseCachePath: t.TempDir(), + SourceContextFunc: buildPullContextfunc(t, imageTagRef), } bundleSource := &source.BundleSource{ Name: "test-bundle", @@ -360,27 +365,29 @@ func newReference(host, repo, tag string) (reference.NamedTagged, error) { return reference.WithTag(ref, tag) } -func buildPullContext(t *testing.T, ref reference.Named) *types.SystemContext { - // Build a containers/image context that allows pulling from the test registry insecurely - registriesConf := sysregistriesv2.V2RegistriesConf{Registries: []sysregistriesv2.Registry{ - { - Prefix: reference.Domain(ref), - Endpoint: sysregistriesv2.Endpoint{ - Location: reference.Domain(ref), - Insecure: true, +func buildPullContextfunc(t *testing.T, ref reference.Named) func(_ logr.Logger) (*types.SystemContext, error) { + return func(_ logr.Logger) (*types.SystemContext, error) { + // Build a containers/image context that allows pulling from the test registry insecurely + registriesConf := sysregistriesv2.V2RegistriesConf{Registries: []sysregistriesv2.Registry{ + { + Prefix: reference.Domain(ref), + Endpoint: sysregistriesv2.Endpoint{ + Location: reference.Domain(ref), + Insecure: true, + }, }, - }, - }} - configDir := t.TempDir() - registriesConfPath := filepath.Join(configDir, "registries.conf") - f, err := os.Create(registriesConfPath) - require.NoError(t, err) - - enc := toml.NewEncoder(f) - require.NoError(t, enc.Encode(registriesConf)) - require.NoError(t, f.Close()) - - return &types.SystemContext{ - SystemRegistriesConfPath: registriesConfPath, + }} + configDir := t.TempDir() + registriesConfPath := filepath.Join(configDir, "registries.conf") + f, err := os.Create(registriesConfPath) + require.NoError(t, err) + + enc := toml.NewEncoder(f) + require.NoError(t, enc.Encode(registriesConf)) + require.NoError(t, f.Close()) + + return &types.SystemContext{ + SystemRegistriesConfPath: registriesConfPath, + }, nil } } From 3002efe08913e4c6c196d206e537bfb26e0e41d1 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Tue, 8 Oct 2024 17:11:18 -0400 Subject: [PATCH 062/694] Update catalogd to v0.31.0 (#1355) Signed-off-by: Todd Short --- docs/api-reference/catalogd-api-reference.md | 6 +++--- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/api-reference/catalogd-api-reference.md b/docs/api-reference/catalogd-api-reference.md index 80a4b327da..f29bd21d4b 100644 --- a/docs/api-reference/catalogd-api-reference.md +++ b/docs/api-reference/catalogd-api-reference.md @@ -97,7 +97,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `source` _[CatalogSource](#catalogsource)_ | source is a required field that allows the user to define the source of a Catalog that contains catalog metadata in the File-Based Catalog (FBC) format.

Below is a minimal example of a ClusterCatalogSpec that sources a catalog from an image:

source:
type: Image
image:
ref: quay.io/operatorhubio/catalog:latest

For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs | | | -| `priority` _integer_ | priority is an optional field that allows the user to define a priority for a ClusterCatalog.
A ClusterCatalog's priority is used by clients as a tie-breaker between ClusterCatalogs that meet the client's requirements.
For example, in the case where multiple ClusterCatalogs provide the same bundle.
A higher number means higher priority. Negative number as also accepted.
When omitted, the default priority is 0. | 0 | | +| `priority` _integer_ | priority is an optional field that allows the user to define a priority for a ClusterCatalog.
A ClusterCatalog's priority is used by clients as a tie-breaker between ClusterCatalogs that meet the client's requirements.
For example, in the case where multiple ClusterCatalogs provide the same bundle.
A higher number means higher priority. Negative numbers are also accepted.
When omitted, the default priority is 0. | 0 | | #### ClusterCatalogStatus @@ -113,8 +113,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterCatalog.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human-readable message that further elaborates on the state of the condition.

The current set of condition types are:
- "Unpacked", epresents whether, or not, the catalog contents have been successfully unpacked.
- "Deleted", represents whether, or not, the catalog contents have been successfully deleted.

The current set of reasons are:
- "UnpackPending", this reason is set on the "Unpack" condition when unpacking the catalog has not started.
- "Unpacking", this reason is set on the "Unpack" condition when the catalog is being unpacked.
- "UnpackSuccessful", this reason is set on the "Unpack" condition when unpacking the catalog is successful and the catalog metadata is available to the cluster.
- "FailedToStore", this reason is set on the "Unpack" condition when an error has been encountered while storing the contents of the catalog.
- "FailedToDelete", this reason is set on the "Delete" condition when an error has been encountered while deleting the contents of the catalog. | | | -| `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source based on the source type.

Below is an example of a resolved source for an image source:
resolvedSource:

image:
lastPollAttempt: "2024-09-10T12:22:13Z"
lastUnpacked: "2024-09-10T12:22:13Z"
ref: quay.io/operatorhubio/catalog:latest
resolvedRef: quay.io/operatorhubio/catalog@sha256:c7392b4be033da629f9d665fec30f6901de51ce3adebeff0af579f311ee5cf1b
type: Image | | | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterCatalog.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human-readable message that further elaborates on the state of the condition.

The current set of condition types are:
- "Serving", which represents whether or not the contents of the catalog are being served via the HTTP(S) web server.
- "Progressing", which represents whether or not the ClusterCatalog is progressing towards a new state.

The current set of reasons are:
- "Succeeded", this reason is set on the "Progressing" condition when progressing to a new state is successful.
- "Blocked", this reason is set on the "Progressing" condition when the ClusterCatalog controller has encountered an error that requires manual intervention for recovery.
- "Retrying", this reason is set on the "Progressing" condition when the ClusterCatalog controller has encountered an error that might be resolvable on subsequent reconciliation attempts.
- "Available", this reason is set on the "Serving" condition when the contents of the ClusterCatalog are being served via an endpoint on the HTTP(S) web server.
- "Unavailable", this reason is set on the "Serving" condition when there is not an endpoint on the HTTP(S) web server that is serving the contents of the ClusterCatalog. | | | +| `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source based on the source type.

Below is an example of a resolved source for an image source:
resolvedSource:

image:
lastSuccessfulPollAttempt: "2024-09-10T12:22:13Z"
ref: quay.io/operatorhubio/catalog@sha256:c7392b4be033da629f9d665fec30f6901de51ce3adebeff0af579f311ee5cf1b
type: Image | | | | `contentURL` _string_ | contentURL is a cluster-internal URL from which on-cluster components
can read the content of a catalog | | | | `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastUnpacked represents the time when the
ClusterCatalog object was last unpacked successfully. | | | diff --git a/go.mod b/go.mod index bf049d9d9e..24965fe7e4 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.30.0 + github.com/operator-framework/catalogd v0.31.0 github.com/operator-framework/helm-operator-plugins v0.5.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index 4d792213cf..18e8be8d32 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.30.0 h1:uW5fGZ5vJlUy9n26B13WZLjc3OMrvr2gM4FQDStUd2Y= -github.com/operator-framework/catalogd v0.30.0/go.mod h1:mSyAemlhvngQPylBU1nY/57syccOv1u9biyovhlRe08= +github.com/operator-framework/catalogd v0.31.0 h1:mP7Z38z8nkCyLXL4RgoHR7uosLw+evLJBs3z2eb2AJ4= +github.com/operator-framework/catalogd v0.31.0/go.mod h1:mSyAemlhvngQPylBU1nY/57syccOv1u9biyovhlRe08= github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= From 855834975f0ef42b0c5b71c3fc641209554d0677 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Wed, 9 Oct 2024 14:59:53 -0400 Subject: [PATCH 063/694] Add checks for helm deployment status (#1349) Don't allow an update to occur when the deployment is still installing. Signed-off-by: Todd Short --- .../clusterextension_controller.go | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index 2601d97f08..bb3a1494b7 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -25,6 +25,7 @@ import ( "time" "github.com/go-logr/logr" + "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/storage/driver" "k8s.io/apimachinery/pkg/api/equality" apimeta "k8s.io/apimachinery/pkg/api/meta" @@ -246,6 +247,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp Ref: resolvedBundle.Image, }, } + l.Info("unpacking resolved bundle") unpackResult, err := r.Unpacker.Unpack(ctx, bundleSource) if err != nil { @@ -470,16 +472,26 @@ func (d *DefaultInstalledBundleGetter) GetInstalledBundle(ctx context.Context, e return nil, err } - release, err := cl.Get(ext.GetName()) + rel, err := cl.Get(ext.GetName()) if err != nil && !errors.Is(err, driver.ErrReleaseNotFound) { return nil, err } - if release == nil { + if rel == nil { return nil, nil } + switch rel.Info.Status { + case release.StatusUnknown: + return nil, fmt.Errorf("installation status is unknown") + case release.StatusDeployed, release.StatusUninstalled, release.StatusSuperseded, release.StatusFailed: + case release.StatusUninstalling, release.StatusPendingInstall, release.StatusPendingRollback, release.StatusPendingUpgrade: + return nil, fmt.Errorf("installation is still pending: %s", rel.Info.Status) + default: + return nil, fmt.Errorf("unknown installation status: %s", rel.Info.Status) + } + return &ocv1alpha1.BundleMetadata{ - Name: release.Labels[labels.BundleNameKey], - Version: release.Labels[labels.BundleVersionKey], + Name: rel.Labels[labels.BundleNameKey], + Version: rel.Labels[labels.BundleVersionKey], }, nil } From d262e3228497d516230c5d8e1ea087358866f1ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:43:08 +0200 Subject: [PATCH 064/694] :seedling: Bump charset-normalizer from 3.3.2 to 3.4.0 (#1356) Bumps [charset-normalizer](https://github.com/Ousret/charset_normalizer) from 3.3.2 to 3.4.0. - [Release notes](https://github.com/Ousret/charset_normalizer/releases) - [Changelog](https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md) - [Commits](https://github.com/Ousret/charset_normalizer/compare/3.3.2...3.4.0) --- updated-dependencies: - dependency-name: charset-normalizer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f36f0bf371..46492865ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ Babel==2.16.0 beautifulsoup4==4.12.3 certifi==2024.8.30 -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 click==8.1.7 colorama==0.4.6 cssselect==1.2.0 From 008ca3399a38e812dc07f627f4d6506276bdc636 Mon Sep 17 00:00:00 2001 From: Rashmi Khanna Date: Fri, 11 Oct 2024 02:54:06 +0530 Subject: [PATCH 065/694] =?UTF-8?q?=F0=9F=93=96=20Update=20svc=20name=20to?= =?UTF-8?q?=20catalogd=20service=20(#1365)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * changes to derice minimum service account Signed-off-by: rashmi_kh * remove headers Signed-off-by: rashmi_kh * add details about registry+v1 support * render yml correctly Signed-off-by: rashmi_kh * Update explore-available-content.md * Delete docs/drafts directory --------- Signed-off-by: rashmi_kh --- docs/tutorials/explore-available-content.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/explore-available-content.md b/docs/tutorials/explore-available-content.md index 2364501c11..67ea2127a2 100644 --- a/docs/tutorials/explore-available-content.md +++ b/docs/tutorials/explore-available-content.md @@ -21,7 +21,7 @@ The following examples will show this default behavior, but for simplicity's sak 1. Port forward the catalog server service: ``` terminal - kubectl -n olmv1-system port-forward svc/catalogd-catalogserver 8443:443 + kubectl -n olmv1-system port-forward svc/catalogd-service 8443:443 ``` 2. Return a list of all the extensions in a catalog: From fe40c8e54f63c7fcf0c785bfcbcc97654e32f743 Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Thu, 10 Oct 2024 23:26:05 +0200 Subject: [PATCH 066/694] :book: a few documentation touch-ups (#1351) Signed-off-by: Per Goncalves da Silva Co-authored-by: Per Goncalves da Silva --- docs/index.md | 52 ++++++++++------------ docs/project/olmv1_architecture.md | 71 +++++++++++++++++------------- docs/project/olmv1_limitations.md | 17 ++++--- docs/project/olmv1_roadmap.md | 4 -- 4 files changed, 71 insertions(+), 73 deletions(-) diff --git a/docs/index.md b/docs/index.md index 942cdd938e..373345caab 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,43 +3,37 @@ hide: - toc --- -# Overview +# Operator Lifecycle Manager -Operator Lifecycle Manager (OLM) is an open-source [CNCF](https://www.cncf.io/) project with the mission to manage the -lifecycle of cluster extensions centrally and declaratively on Kubernetes clusters. Its purpose is to make installing, -running, and updating functional extensions to the cluster easy, safe, and reproducible for cluster administrators and PaaS administrators. +The Operator Lifecycle Manager (OLM) is an open-source project under the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/), designed to simplify and centralize the management of Kubernetes cluster extensions. OLM streamlines the process of installing, running, and updating these extensions, making it easier, safer, and more reproducible for cluster and platform administrators alike. -Previously, OLM was focused on a particular type of cluster extension: [Operators](https://operatorhub.io/what-is-an-operator#:~:text=is%20an%20Operator-,What%20is%20an%20Operator%20after%20all%3F,or%20automation%20software%20like%20Ansible.). -Operators are a method of packaging, deploying, and managing a Kubernetes application. An Operator is composed of one or more controllers paired with one or both of the following objects: +Originally, OLM was focused on managing a specific type of extension known as [Operators](https://operatorhub.io/what-is-an-operator#:~:text=is%20an%20Operator-,What%20is%20an%20Operator%20after%20all%3F,or%20automation%20software%20like%20Ansible.), which are powerful tools that automate the management of complex Kubernetes applications. At its core, an Operator is made up of controllers that automate the lifecycle of applications, paired with: -* One or more API extensions -* One or more [CustomResourceDefinitions](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) (CRDs). +- One or more Kubernetes API extensions. +- One or more [CustomResourceDefinitions (CRDs)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/), allowing administrators to define custom resources. -OLM helped define lifecycles for these extensions: from packaging and distribution to installation, configuration, upgrade, and removal. +The purpose of OLM is to manage the lifecycle of these extensions—from their packaging and distribution to installation, updates, and eventual removal—helping administrators ensure stability and security across their clusters. -The first iteration of OLM, termed OLM v0, included several concepts and features targeting the stability, security, and supportability of the life-cycled applications, for instance: +In its first release (OLM v0), the project introduced several important concepts and features aimed at improving the lifecycle management of Kubernetes applications: -* A dependency model that enabled cluster extensions to focus on their primary purpose by delegating out of scope behavior to dependencies -* A constraint model that allowed cluster extension developers to define support limitations such as conflicting extensions, and minimum kubernetes versions -* A namespace-based multi-tenancy model in lieu of namespace-scoped CRDs -* A packaging model in which catalogs of extensions, usually containing the entire version history of each extension, are made available to clusters for cluster users to browse and select from +- **Dependency Model**: Enables extensions to focus on their primary function by delegating non-essential tasks to other dependencies. +- **Constraint Model**: Allows developers to define compatibility constraints such as conflicting extensions or minimum required Kubernetes versions. +- **Namespace-Based Multi-Tenancy**: Provides a multi-tenancy model to manage multiple extensions without the need for namespace-scoped CRDs. +- **Packaging Model**: Distributes extensions through catalogs, allowing users to browse and install extensions, often with access to the full version history. -Since its initial release, OLM has helped catalyse the growth of Operators throughout the Kubernetes ecosystem. [OperatorHub.io](https://operatorhub.io/) -is a popular destination for discovering Operators, and boasts over 300 packages from many different vendors. +Thanks to these innovations, OLM has played a significant role in popularizing Operators throughout the Kubernetes ecosystem. A prime example of its impact is [OperatorHub.io](https://operatorhub.io/), a widely-used platform with over 300 Operators from various vendors, providing users with a central location to discover and install Operators. -## Why are we building OLM v1? +## Why Build OLM v1? -The Operator Lifecycle Manager (OLM) has been in production for over five years, serving as a critical component in managing Kubernetes Operators. -Over this time, the community has gathered valuable insights from real-world usage, identifying both the strengths and limitations of the initial design, -and validating the design's initial assumptions. This process led to a complete redesign and rewrite of OLM that, compared to its predecessor, aims to -provide: +After five years of real-world use, OLM has become an essential part of managing Kubernetes Operators. However, over time, the community has gathered valuable insights, uncovering both the strengths and limitations of OLM v0. These findings have led to a comprehensive redesign and the creation of OLM v1, with several key improvements over the initial version: -* A simpler API surface and mental model -* Less opinionated automation and greater flexibility -* Support for Kubernetes applications beyond only Operators -* Security by default -* Helm Chart support -* GitOps support +- **Simpler API and Mental Model**: Streamlined APIs and a more intuitive design, making it easier to understand and work with. +- **Greater Flexibility**: Less rigid automation, allowing for more customization and broader use cases. +- **Beyond Operators**: Support for a wider range of Kubernetes applications, not limited to Operators. +- **Security by Default**: Enhanced security features out-of-the-box, reducing vulnerabilities. +- **Helm Chart and GitOps Support**: Expanded support for popular Kubernetes tools like Helm and GitOps, broadening the range of integration options. -To learn more about where v1 one came from, and where it's going, please see [Multi-Tenancy Challenges, Lessons Learned, and Design Shifts](project/olmv1_design_decisions.md) -and our feature [Roadmap](project/olmv1_roadmap.md). +For more details on the evolution of OLM and the roadmap for v1, explore the following resources: + +- [Multi-Tenancy Challenges, Lessons Learned, and Design Shifts](project/olmv1_design_decisions.md) +- [OLM v1 Roadmap](project/olmv1_roadmap.md) diff --git a/docs/project/olmv1_architecture.md b/docs/project/olmv1_architecture.md index 1672fae648..f12fc8f03f 100644 --- a/docs/project/olmv1_architecture.md +++ b/docs/project/olmv1_architecture.md @@ -3,16 +3,16 @@ hide: - toc --- -# OLM V1 Architecture +## OLM v1 Architecture -This document describes the OLM v1 architecture. OLM v1 consists of two main components: +This document provides an overview of the architecture of OLM v1, which consists of two primary components: -* [operator-controller](https://github.com/operator-framework/operator-controller) -* [catalogD](https://github.com/operator-framework/catalogd) +1. [operator-controller](https://github.com/operator-framework/operator-controller) +2. [catalogD](https://github.com/operator-framework/catalogd) -The diagram below illustrates the OLM v1 architecture and its components, and the following sections describe each of the components in detail. +The diagram below visually represents the architecture of OLM v1, followed by descriptions of each component and its role within the system. -### Diagram +### Architecture Diagram ```mermaid flowchart TB @@ -53,39 +53,48 @@ flowchart TB A -- pushed to --> B ``` -**Note**: The direction of the arrow indicates the active part of communication i.e. if arrow starts from A and points to B that means A consumes the information from B unless specifically mentioned. +**Note**: The direction of the arrows represents the flow of communication. If an arrow starts from A and points to B, it indicates that A retrieves or consumes information from B, unless otherwise specified. -### Operator-controller: +--- + +### operator-controller + +The `operator-controller` is the core component of OLM v1. Its responsibilities include: -operator-controller is the central component of OLM v1. It is responsible: +- Managing a cache of catalog metadata provided by catalogD through its HTTP server. +- Ensuring the catalog metadata cache is kept up-to-date with the latest catalog state. +- Identifying the appropriate `registry+v1` bundle that meets the constraints defined in the `ClusterExtension` resource (e.g., package name, version, channel) based on the cluster's current state. +- Unpacking and applying bundle manifests (e.g., installing or updating the operator). - * managing a cache of catalog metadata provided by catalogd through its HTTP server - * keeping the catalog metadata cache up-to-date with the current state of the catalogs - * locating the right `registry+v1` bundle, if any, that meet the constraints expressed in the `ClusterExtension` resource, such as package name, version range, channel, etc. given the current state of the cluster - * unpacking the bundle - * applying the bundle manifests: installing or updating the content. - - It has three main sub-components: +The operator-controller has three key sub-components: - * Cluster Extension Controller: - * Queries the catalogd (catalogd HTTP Server) to get catalog information. - * Once received the catalog information is saved to catalog-cache. The cache will be updated automatically if a Catalog is noticed to have a different resolved image reference. - * Reaches out to the registry to download the bundle container images, saves it to the bundle cache, unpacks it and applies the bundle manifests to the cluster. - * It is also Responsible for figuring out which bundle to upgrade - * Resolver: - * Helps the cluster extension controller to filter the bundle reference after applying the user restrictions (e.g. name, priority etc) and returns the bundle reference to the extension controller. - * Bundle Cache: - * Bundle cache returns the cache for the bundle. If a cache does not already exist, a new one will be created. +1. **Cluster Extension Controller**: + - Queries catalogD (via its HTTP server) to retrieve catalog information. + - Saves catalog information in the catalog cache and automatically updates the cache if a catalog has a new image reference. + - Downloads bundle container images from the registry, saves them to the bundle cache, unpacks them, and applies the bundle manifests to the cluster. + - Handles bundle upgrades by determining which bundle is the correct one to apply. -### Catalogd: +2. **Resolver**: + - Assists the Cluster Extension Controller by filtering bundle references according to user-defined restrictions (e.g., package name, priority). It returns the filtered bundle reference to the extension controller. + +3. **Bundle Cache**: + - Stores previously unpacked bundles. If a bundle is not already cached, it downloads and caches it for future use. + +--- -Catalogd unpacks [file-based catalog (FBC)](https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs) content that is packaged and shipped in container images, for consumption by clients on-clusters (unpacking from other sources, like git repos, OCI artifacts etc, are in the roadmap for catalogD). It serves the extension metadata, provided by the extension authors, found in the FBC, making it possible for on-cluster clients to discover installable content. +### catalogd -* Catalogd can be broken down in to three sub-components i.e. ClusterCatalog controller, catalogd http server, catalogd content cache. -* Catalog controller is responsible for pulling FBC based catalog images from registry and unpacking them into the catalog content cache. It is also responsible for reconciling the latest changes in the cluster catalog. -* Catalogd http server is responsible for serving catalog information to clients e.g. cluster extension controller. -* Catalogd content cache is maintained by the catalog controller and used by the catalogd http server to answer queries from clients. +catalogd is responsible for unpacking [file-based catalog (FBC)](https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs) content, which is packaged and delivered as container images. It allows on-cluster clients to discover installable content by providing access to this metadata. In the future, catalogD will also support other sources like Git repositories and OCI artifacts. +catalogd has three main sub-components: +1. **ClusterCatalog Controller**: + - Pulls FBC-based catalog images from the registry and unpacks them into the catalog content cache. + - Reconciles any changes in the catalog and ensures the latest content is reflected in the cluster. + +2. **CatalogD HTTP Server**: + - Serves catalog information to clients, such as the Cluster Extension Controller. +3. **CatalogD Content Cache**: + - A cache maintained by the Catalog Controller that stores unpacked catalog data, which the CatalogD HTTP Server uses to respond to client queries. diff --git a/docs/project/olmv1_limitations.md b/docs/project/olmv1_limitations.md index 172d8cbb5f..26e2340ff5 100644 --- a/docs/project/olmv1_limitations.md +++ b/docs/project/olmv1_limitations.md @@ -3,23 +3,22 @@ hide: - toc --- -## OLM v0 Extension Support +## Content Support -Currently, OLM v1 supports installing cluster extensions that meet the following criteria: - -* The extension must support installation via the `AllNamespaces` install mode. -* The extension must not use webhooks. -* The extension must not declare dependencies using any of the following file-based catalog properties: +Currently, OLM v1 only supports installing operators packaged in [OLM v0 bundles](https://olm.operatorframework.io/docs/tasks/creating-operator-bundle/) +, also known as `registry+v1` bundles. Additionally, the bundled operator, or cluster extension: +* **must** support installation via the `AllNamespaces` install mode. +* **must not** use webhooks. +* **must not** declare dependencies using any of the following file-based catalog properties: * `olm.gvk.required` * `olm.package.required` * `olm.constraint` -When you install an extension, OLM v1 validates that the bundle you want to install meets these constraints. If you try to install an extension that does not meet these constraints, an error message is printed in the cluster extension's conditions. +OLM v1 verifies these criteria at install time and will surface violations in the `ClusterExtensions`'s `.status.conditions`. !!! important OLM v1 does not support the `OperatorConditions` API introduced in legacy OLM. - Currently, there is no testing to validate against this constraint. If an extension uses the `OperatorConditions` API, the extension does not install correctly. Most extensions that rely on this API fail at start time, but some might fail during reconcilation. - + Currently, there is no testing to validate against this constraint. If an extension uses the `OperatorConditions` API, the extension does not install correctly. Most extensions that rely on this API fail at start time, but some might fail during reconcilation. diff --git a/docs/project/olmv1_roadmap.md b/docs/project/olmv1_roadmap.md index 5a0542a3e5..bba5871538 100644 --- a/docs/project/olmv1_roadmap.md +++ b/docs/project/olmv1_roadmap.md @@ -153,7 +153,3 @@ OLM 1.0 does not support managing bundles or extension versions that do not supp - Migration scripting is provided to mass-convert existing installed extensions (“Subscription” / “OperatorGroup” objects) on existing clusters to the new OLM 1.0 model assuming they are compatible - Extension authors that are also SRE/Managed PaaS administrators are incentivized to make their extension compatible with the requirements of OLM 1.0 to reap the operational benefits - -# TODO -- Definition of "extension" -- Does OLM become ELM? Does this provide of provisioning bundles that do not add APIs? From f2715c34f5f2ada3655de91560490a70ff47a803 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Thu, 10 Oct 2024 17:34:09 -0400 Subject: [PATCH 067/694] Fix typo for a link ref in derive-service-account.md (#1366) --- docs/howto/derive-service-account.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/howto/derive-service-account.md b/docs/howto/derive-service-account.md index 599fc103a8..ac0481ffc7 100644 --- a/docs/howto/derive-service-account.md +++ b/docs/howto/derive-service-account.md @@ -23,7 +23,7 @@ The service account must have permissions to: Additionally, for clusters that use the [OwnerReferencesPermissionEnforcement](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement) admission plug-in, the service account must also have permissions to: - update finalizers on the ClusterExtension to be able to set blockOwnerDeletion and ownerReferences -It is good security practice to follow the [principle of least privilege(https://en.wikipedia.org/wiki/Principle_of_least_privilege)], and scope permissions to specific resource names, wherever possible. +It is good security practice to follow the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), and scope permissions to specific resource names, wherever possible. Keep in mind, that it is not possible to scope `create`, `list`, and `watch` permissions to specific resource names. Depending on the scope, each permission will need to be added to either a `ClusterRole` or a `Role` and then bound to the service account with a `ClusterRoleBinding` or a `RoleBinding`. @@ -349,4 +349,4 @@ kubectl create clusterrolebinding my-cluster-extension-installer-role-binding \ In the spirit of making this process more tenable until the proper tools are in place, the scripts in [hack/tools/catalogs](https://github.com/operator-framework/operator-controller/blob/main/hack/tools/catalogs) were created to help the user navigate and search catalogs as well as to generate the minimal RBAC requirements. These tools are offered as is, with no guarantees on their correctness, -support, or maintenance. For more information, see [Hack Catalog Tools](https://github.com/operator-framework/operator-controller/blob/main/hack/tools/catalogs/README.md). \ No newline at end of file +support, or maintenance. For more information, see [Hack Catalog Tools](https://github.com/operator-framework/operator-controller/blob/main/hack/tools/catalogs/README.md). From 78b586aa721598e5da4b732572cbc1e93507daa9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 21:03:59 -0400 Subject: [PATCH 068/694] :seedling: Bump mkdocs-material from 9.5.39 to 9.5.40 (#1363) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.39 to 9.5.40. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.39...9.5.40) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 46492865ee..7188a2a3a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ markdown2==2.5.0 MarkupSafe==2.1.5 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.5.39 +mkdocs-material==9.5.40 mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 From fe84c5f40fe54068a5c9614600b3c75b9b151d21 Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:13:36 +0200 Subject: [PATCH 069/694] Fix pulling signed images (#1369) This fixes "pushing signatures for OCI images is not supported" error when working with signed source images. If policy context requires signature validation for a registry we will still be performing it on pull, but we will be removing source signatures when copying into a temporary OCI layout for unpacking. Signed-off-by: Mikalai Radchuk --- internal/rukpak/source/containers_image.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/rukpak/source/containers_image.go b/internal/rukpak/source/containers_image.go index 132f6fcd3b..76c5812c8c 100644 --- a/internal/rukpak/source/containers_image.go +++ b/internal/rukpak/source/containers_image.go @@ -123,6 +123,12 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour ////////////////////////////////////////////////////// if _, err := copy.Image(ctx, policyContext, layoutRef, dockerRef, ©.Options{ SourceCtx: srcCtx, + // We use the OCI layout as a temporary storage and + // pushing signatures for OCI images is not supported + // so we remove the source signatures when copying. + // Signature validation will still be performed + // accordingly to a provided policy context. + RemoveSignatures: true, }); err != nil { return nil, fmt.Errorf("error copying image: %w", err) } From e5ecec7bba1a5db51dba3a63cc0637ade4f7bdd3 Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:38:20 +0200 Subject: [PATCH 070/694] Bump catalogd to v0.32.0 (#1370) Signed-off-by: Mikalai Radchuk --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 24965fe7e4..707cc08b40 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.31.0 + github.com/operator-framework/catalogd v0.32.0 github.com/operator-framework/helm-operator-plugins v0.5.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 @@ -139,7 +139,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 // indirect github.com/k14s/ytt v0.36.0 // indirect - github.com/klauspost/compress v1.17.10 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect diff --git a/go.sum b/go.sum index 18e8be8d32..4abf6d1cff 100644 --- a/go.sum +++ b/go.sum @@ -412,8 +412,8 @@ github.com/k14s/ytt v0.36.0/go.mod h1:awQ3bHBk1qT2Xn3GJVdmaLss2khZOIBBKFd2TNXZNM github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0= -github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -535,8 +535,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.31.0 h1:mP7Z38z8nkCyLXL4RgoHR7uosLw+evLJBs3z2eb2AJ4= -github.com/operator-framework/catalogd v0.31.0/go.mod h1:mSyAemlhvngQPylBU1nY/57syccOv1u9biyovhlRe08= +github.com/operator-framework/catalogd v0.32.0 h1:VKD+7wfEF6CnJgR4aUYyT85KP2Te7zjhaPvgvWy25Uw= +github.com/operator-framework/catalogd v0.32.0/go.mod h1:FrFSCwRXr4aPslcXIv48dan5AdM37k/B9tK/RpdvZCU= github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= From 747842198942eefd8eeb732f3aaf1f7191c44f51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:42:32 +0200 Subject: [PATCH 071/694] :seedling: Bump github.com/containerd/containerd from 1.7.22 to 1.7.23 (#1377) Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.22 to 1.7.23. - [Release notes](https://github.com/containerd/containerd/releases) - [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md) - [Commits](https://github.com/containerd/containerd/compare/v1.7.22...v1.7.23) --- updated-dependencies: - dependency-name: github.com/containerd/containerd dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 707cc08b40..b49e0db0ee 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/BurntSushi/toml v1.4.0 github.com/Masterminds/semver/v3 v3.3.0 github.com/blang/semver/v4 v4.0.0 - github.com/containerd/containerd v1.7.22 + github.com/containerd/containerd v1.7.23 github.com/containers/image/v5 v5.32.2 github.com/fsnotify/fsnotify v1.7.0 github.com/go-logr/logr v1.4.2 @@ -59,7 +59,7 @@ require ( github.com/containerd/cgroups/v3 v3.0.3 // indirect github.com/containerd/containerd/api v1.7.19 // indirect github.com/containerd/continuity v0.4.2 // indirect - github.com/containerd/errdefs v0.1.0 // indirect + github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect diff --git a/go.sum b/go.sum index 4abf6d1cff..8d591b5043 100644 --- a/go.sum +++ b/go.sum @@ -85,14 +85,14 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= -github.com/containerd/containerd v1.7.22 h1:nZuNnNRA6T6jB975rx2RRNqqH2k6ELYKDZfqTHqwyy0= -github.com/containerd/containerd v1.7.22/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g= +github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= +github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA= github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= -github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= +github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4= +github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= From f9bce7c955b50a77ba4a04f0cc1345103d20a15e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:43:22 +0000 Subject: [PATCH 072/694] :seedling: Bump markdown2 from 2.5.0 to 2.5.1 (#1362) Bumps [markdown2](https://github.com/trentm/python-markdown2) from 2.5.0 to 2.5.1. - [Changelog](https://github.com/trentm/python-markdown2/blob/master/CHANGES.md) - [Commits](https://github.com/trentm/python-markdown2/compare/2.5.0...2.5.1) --- updated-dependencies: - dependency-name: markdown2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7188a2a3a0..37932ccc4f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ idna==3.10 Jinja2==3.1.4 lxml==5.3.0 Markdown==3.7 -markdown2==2.5.0 +markdown2==2.5.1 MarkupSafe==2.1.5 mergedeep==1.3.4 mkdocs==1.6.1 From b7674d8514065e3790f48a37c8af48695f44cbef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:55:41 +0200 Subject: [PATCH 073/694] :seedling: Bump helm.sh/helm/v3 from 3.16.1 to 3.16.2 (#1364) Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.16.1 to 3.16.2. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.16.1...v3.16.2) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index b49e0db0ee..f43a9e2a3f 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 gopkg.in/yaml.v2 v2.4.0 - helm.sh/helm/v3 v3.16.1 + helm.sh/helm/v3 v3.16.2 k8s.io/api v0.31.1 k8s.io/apiextensions-apiserver v0.31.1 k8s.io/apimachinery v0.31.1 @@ -243,7 +243,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiserver v0.31.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/kubectl v0.31.0 // indirect + k8s.io/kubectl v0.31.1 // indirect oras.land/oras-go v1.2.5 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/go.sum b/go.sum index 8d591b5043..bbc6afea63 100644 --- a/go.sum +++ b/go.sum @@ -982,8 +982,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -helm.sh/helm/v3 v3.16.1 h1:cER6tI/8PgUAsaJaQCVBUg3VI9KN4oVaZJgY60RIc0c= -helm.sh/helm/v3 v3.16.1/go.mod h1:r+xBHHP20qJeEqtvBXMf7W35QDJnzY/eiEBzt+TfHps= +helm.sh/helm/v3 v3.16.2 h1:Y9v7ry+ubQmi+cb5zw1Llx8OKHU9Hk9NQ/+P+LGBe2o= +helm.sh/helm/v3 v3.16.2/go.mod h1:SyTXgKBjNqi2NPsHCW5dDAsHqvGIu0kdNYNH9gQaw70= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1007,8 +1007,8 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/kubectl v0.31.0 h1:kANwAAPVY02r4U4jARP/C+Q1sssCcN/1p9Nk+7BQKVg= -k8s.io/kubectl v0.31.0/go.mod h1:pB47hhFypGsaHAPjlwrNbvhXgmuAr01ZBvAIIUaI8d4= +k8s.io/kubectl v0.31.1 h1:ih4JQJHxsEggFqDJEHSOdJ69ZxZftgeZvYo7M/cpp24= +k8s.io/kubectl v0.31.1/go.mod h1:aNuQoR43W6MLAtXQ/Bu4GDmoHlbhHKuyD49lmTC8eJM= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= From 8e0efaa757200f136f533b0b05b27ebc10686575 Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Wed, 16 Oct 2024 10:47:42 +0200 Subject: [PATCH 074/694] Remove `EnableExtensionApi` feature gate (#1375) Signed-off-by: Mikalai Radchuk --- internal/features/features.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/features/features.go b/internal/features/features.go index e930fbbaac..329737a960 100644 --- a/internal/features/features.go +++ b/internal/features/features.go @@ -10,7 +10,6 @@ const ( // Ex: SomeFeature featuregate.Feature = "SomeFeature" ForceSemverUpgradeConstraints featuregate.Feature = "ForceSemverUpgradeConstraints" - EnableExtensionAPI featuregate.Feature = "EnableExtensionApi" ) var operatorControllerFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ @@ -18,7 +17,6 @@ var operatorControllerFeatureGates = map[featuregate.Feature]featuregate.Feature // Ex: SomeFeature: {...} ForceSemverUpgradeConstraints: {Default: false, PreRelease: featuregate.Alpha}, - EnableExtensionAPI: {Default: false, PreRelease: featuregate.Alpha}, } var OperatorControllerFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate() From 0dd2305178f84ca92a6489a0630d3788016fca75 Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Thu, 17 Oct 2024 12:10:08 +0200 Subject: [PATCH 075/694] Update catalog-queries.md (#1385) --- docs/howto/catalog-queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/howto/catalog-queries.md b/docs/howto/catalog-queries.md index 7a25863ecf..4e6f260d79 100644 --- a/docs/howto/catalog-queries.md +++ b/docs/howto/catalog-queries.md @@ -15,7 +15,7 @@ curl -k https://localhost:8443/catalogs/operatorhubio/all.json | Available packages in a catalog : ``` terminal -jq -s '.[] | select( .schema == "olm.package") +jq -s '.[] | select( .schema == "olm.package")' ``` Packages that support `AllNamespaces` install mode and do not use webhooks From 79f42d5a439a1d30bc331d198d687cc8729768e9 Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Thu, 17 Oct 2024 12:29:41 +0200 Subject: [PATCH 076/694] Address goreleaser deprecated flags (#1386) Signed-off-by: Per Goncalves da Silva Co-authored-by: Per Goncalves da Silva --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 43ceeca64b..f11b8db035 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -45,7 +45,7 @@ jobs: elif [[ $GITHUB_REF == refs/heads/* ]]; then # Branch build. echo IMAGE_TAG="$(echo "${GITHUB_REF#refs/heads/}" | sed -r 's|/+|-|g')" >> $GITHUB_ENV - echo GORELEASER_ARGS="--clean --skip-validate" >> $GITHUB_ENV + echo GORELEASER_ARGS="--clean --skip=validate" >> $GITHUB_ENV elif [[ $GITHUB_REF == refs/pull/* ]]; then # PR build. echo IMAGE_TAG="pr-$(echo "${GITHUB_REF}" | sed -E 's|refs/pull/([^/]+)/?.*|\1|')" >> $GITHUB_ENV From 8ae6ca096695eb35f1ccf2b3ef3490fc1ce5d5d0 Mon Sep 17 00:00:00 2001 From: Brett Tofel Date: Thu, 17 Oct 2024 14:22:50 -0400 Subject: [PATCH 077/694] =?UTF-8?q?=F0=9F=8C=B1=20Use=20catalog=20availabi?= =?UTF-8?q?lity=20(#1359)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add support for catalogs with Spec.Availability Signed-off-by: Brett Tofel * Revert local go.mod change Signed-off-by: Brett Tofel * Error message change to not be misleading Co-authored-by: Joe Lanford * Fixups after accepting suggested error changes Signed-off-by: Brett Tofel * Changes for PR comments... https://github.com/operator-framework/operator-controller/pull/1359#discussion_r1801073127 https://github.com/operator-framework/operator-controller/pull/1359#discussion_r1801070984 https://github.com/operator-framework/operator-controller/pull/1359#discussion_r1801064220 https://github.com/operator-framework/operator-controller/pull/1359#discussion_r1801061282 https://github.com/operator-framework/operator-controller/pull/1359#discussion_r1801054976 https://github.com/operator-framework/operator-controller/pull/1359#discussion_r1801046747 Signed-off-by: Brett Tofel * Change to preprocess catalogs for enabled... And delete unneeded test for mixed case priority+disablement Signed-off-by: Brett Tofel * Remove TestCatalogWithoutAvailabilityIsEnabled Signed-off-by: Brett Tofel * Bump catalogd dep to v0.33.0 (and rebase) Signed-off-by: Brett Tofel * After make crd-ref-docs Signed-off-by: Brett Tofel * After gci run Signed-off-by: Brett Tofel --------- Signed-off-by: Brett Tofel Co-authored-by: Joe Lanford --- docs/api-reference/catalogd-api-reference.md | 1 + go.mod | 4 +- go.sum | 8 +-- internal/resolve/catalog.go | 20 +++++- internal/resolve/catalog_test.go | 72 ++++++++++++++++++++ 5 files changed, 98 insertions(+), 7 deletions(-) diff --git a/docs/api-reference/catalogd-api-reference.md b/docs/api-reference/catalogd-api-reference.md index f29bd21d4b..fa52fbc00d 100644 --- a/docs/api-reference/catalogd-api-reference.md +++ b/docs/api-reference/catalogd-api-reference.md @@ -98,6 +98,7 @@ _Appears in:_ | --- | --- | --- | --- | | `source` _[CatalogSource](#catalogsource)_ | source is a required field that allows the user to define the source of a Catalog that contains catalog metadata in the File-Based Catalog (FBC) format.

Below is a minimal example of a ClusterCatalogSpec that sources a catalog from an image:

source:
type: Image
image:
ref: quay.io/operatorhubio/catalog:latest

For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs | | | | `priority` _integer_ | priority is an optional field that allows the user to define a priority for a ClusterCatalog.
A ClusterCatalog's priority is used by clients as a tie-breaker between ClusterCatalogs that meet the client's requirements.
For example, in the case where multiple ClusterCatalogs provide the same bundle.
A higher number means higher priority. Negative numbers are also accepted.
When omitted, the default priority is 0. | 0 | | +| `availability` _string_ | Availability is an optional field that allows users to define whether the ClusterCatalog is utilized by the operator-controller.

Allowed values are : ["Enabled", "Disabled"].
If set to "Enabled", the catalog will be used for updates, serving contents, and package installations.

If set to "Disabled", catalogd will stop serving the catalog and the cached data will be removed.

If unspecified, the default value is "Enabled" | Enabled | Enum: [Disabled Enabled]
| #### ClusterCatalogStatus diff --git a/go.mod b/go.mod index f43a9e2a3f..ebd0d93b88 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.32.0 + github.com/operator-framework/catalogd v0.33.0 github.com/operator-framework/helm-operator-plugins v0.5.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 @@ -179,7 +179,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/proglottis/gpgme v0.1.3 // indirect - github.com/prometheus/client_golang v1.20.4 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect diff --git a/go.sum b/go.sum index bbc6afea63..ae31449635 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.32.0 h1:VKD+7wfEF6CnJgR4aUYyT85KP2Te7zjhaPvgvWy25Uw= -github.com/operator-framework/catalogd v0.32.0/go.mod h1:FrFSCwRXr4aPslcXIv48dan5AdM37k/B9tK/RpdvZCU= +github.com/operator-framework/catalogd v0.33.0 h1:hnLIFykO1FkjOAUFRPuYRIHQTE0oBF9jkGmWjKhxniQ= +github.com/operator-framework/catalogd v0.33.0/go.mod h1:anZurjcFMBvbkuyqlJ98v9z+yjniPKqmhlyitk9DuBQ= github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= @@ -569,8 +569,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= diff --git a/internal/resolve/catalog.go b/internal/resolve/catalog.go index 0c9dac762f..de210fe3cd 100644 --- a/internal/resolve/catalog.go +++ b/internal/resolve/catalog.go @@ -13,6 +13,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" "github.com/operator-framework/operator-registry/alpha/declcfg" @@ -231,20 +232,37 @@ func isDeprecated(bundle declcfg.Bundle, deprecation *declcfg.Deprecation) bool type CatalogWalkFunc func(context.Context, *catalogd.ClusterCatalog, *declcfg.DeclarativeConfig, error) error -func CatalogWalker(listCatalogs func(context.Context, ...client.ListOption) ([]catalogd.ClusterCatalog, error), getPackage func(context.Context, *catalogd.ClusterCatalog, string) (*declcfg.DeclarativeConfig, error)) func(ctx context.Context, packageName string, f CatalogWalkFunc, catalogListOpts ...client.ListOption) error { +func CatalogWalker( + listCatalogs func(context.Context, ...client.ListOption) ([]catalogd.ClusterCatalog, error), + getPackage func(context.Context, *catalogd.ClusterCatalog, string) (*declcfg.DeclarativeConfig, error), +) func(ctx context.Context, packageName string, f CatalogWalkFunc, catalogListOpts ...client.ListOption) error { return func(ctx context.Context, packageName string, f CatalogWalkFunc, catalogListOpts ...client.ListOption) error { + l := log.FromContext(ctx) catalogs, err := listCatalogs(ctx, catalogListOpts...) if err != nil { return fmt.Errorf("error listing catalogs: %w", err) } + // Remove disabled catalogs from consideration + catalogs = slices.DeleteFunc(catalogs, func(c catalogd.ClusterCatalog) bool { + if c.Spec.Availability == "Disabled" { + l.Info("excluding ClusterCatalog from resolution process since it is disabled", "catalog", c.Name) + return true + } + return false + }) + for i := range catalogs { cat := &catalogs[i] + + // process enabled catalogs fbc, fbcErr := getPackage(ctx, cat, packageName) + if walkErr := f(ctx, cat, fbc, fbcErr); walkErr != nil { return walkErr } } + return nil } } diff --git a/internal/resolve/catalog_test.go b/internal/resolve/catalog_test.go index bce3b5778e..4856efc7e3 100644 --- a/internal/resolve/catalog_test.go +++ b/internal/resolve/catalog_test.go @@ -963,3 +963,75 @@ func TestMultipleChannels(t *testing.T) { assert.Equal(t, bsemver.MustParse("2.0.0"), *gotVersion) assert.Equal(t, ptr.To(packageDeprecation(pkgName)), gotDeprecation) } + +func TestAllCatalogsDisabled(t *testing.T) { + pkgName := randPkg() + listCatalogs := func(ctx context.Context, options ...client.ListOption) ([]catalogd.ClusterCatalog, error) { + return []catalogd.ClusterCatalog{ + { + Spec: catalogd.ClusterCatalogSpec{ + Availability: "Disabled", + }, + }, + { + Spec: catalogd.ClusterCatalogSpec{ + Availability: "Disabled", + }, + }, + }, nil + } + + getPackage := func(ctx context.Context, cat *catalogd.ClusterCatalog, packageName string) (*declcfg.DeclarativeConfig, error) { + panic("getPackage should never be called when all catalogs are disabled") + } + + r := CatalogResolver{ + WalkCatalogsFunc: CatalogWalker(listCatalogs, getPackage), + } + + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + _, _, _, err := r.Resolve(context.Background(), ce, nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "no bundles found for package") +} + +func TestSomeCatalogsDisabled(t *testing.T) { + pkgName := randPkg() + listCatalogs := func(ctx context.Context, options ...client.ListOption) ([]catalogd.ClusterCatalog, error) { + return []catalogd.ClusterCatalog{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "enabledCatalog", + }, + Spec: catalogd.ClusterCatalogSpec{ + Priority: 1, // Higher priority + Availability: "Enabled", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "disabledCatalog", + }, + Spec: catalogd.ClusterCatalogSpec{ + Priority: 0, // Lower priority (but disabled) + Availability: "Disabled", + }, + }, + }, nil + } + + getPackage := func(ctx context.Context, cat *catalogd.ClusterCatalog, packageName string) (*declcfg.DeclarativeConfig, error) { + // Only enabled catalog should be processed + return genPackage(pkgName), nil + } + + r := CatalogResolver{ + WalkCatalogsFunc: CatalogWalker(listCatalogs, getPackage), + } + + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + gotBundle, gotVersion, _, err := r.Resolve(context.Background(), ce, nil) + require.NoError(t, err) + require.NotNil(t, gotBundle) + require.Equal(t, bsemver.MustParse("3.0.0"), *gotVersion) +} From 30838799bbe2fd9bce61a359a291d653a1990c90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:37:45 -0700 Subject: [PATCH 078/694] :seedling: Bump mkdocs-material from 9.5.40 to 9.5.41 (#1381) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.40 to 9.5.41. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.40...9.5.41) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 37932ccc4f..b44b225bec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ markdown2==2.5.1 MarkupSafe==2.1.5 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.5.40 +mkdocs-material==9.5.41 mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 From 8a0f25d980fa5de929890ba20935e4a6e771c54b Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Fri, 18 Oct 2024 16:27:19 -0400 Subject: [PATCH 079/694] (bugfix): fix a bug in the containers image source (#1395) where we always pulled image references using the canonical reference. Now we pull images using the reference provided to the source. Using only the canonical reference resulted in not respecting some mirroring configurations related to tags because we never used tag-based references. Signed-off-by: everettraven --- internal/rukpak/source/containers_image.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/rukpak/source/containers_image.go b/internal/rukpak/source/containers_image.go index 76c5812c8c..22f072da21 100644 --- a/internal/rukpak/source/containers_image.go +++ b/internal/rukpak/source/containers_image.go @@ -80,7 +80,7 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour // copy.Image can concurrently pull all the layers. // ////////////////////////////////////////////////////// - dockerRef, err := docker.NewReference(canonicalRef) + dockerRef, err := docker.NewReference(imgRef) if err != nil { return nil, fmt.Errorf("error creating source reference: %w", err) } From bfa0435e7f54d23464f4811be3a419c2c88d0c7d Mon Sep 17 00:00:00 2001 From: Jordan Keister Date: Fri, 18 Oct 2024 15:58:36 -0500 Subject: [PATCH 080/694] updated to use catalogd v0.34.0 (#1396) see [catalogd v0.34.0 release notes](https://github.com/operator-framework/catalogd/releases/tag/v0.34.0) for more details Signed-off-by: Jordan Keister --- docs/api-reference/catalogd-api-reference.md | 18 +++++++++++++++++- docs/api-reference/catalogd-webserver.md | 15 +++++++++------ docs/howto/catalog-queries.md | 2 +- docs/tutorials/add-catalog.md | 3 ++- docs/tutorials/explore-available-content.md | 8 ++++---- go.mod | 2 +- go.sum | 4 ++-- hack/tools/catalogs/download-catalog | 16 +++++++++------- internal/catalogmetadata/client/client.go | 16 +++++++++++++++- internal/catalogmetadata/client/client_test.go | 4 +++- 10 files changed, 63 insertions(+), 25 deletions(-) diff --git a/docs/api-reference/catalogd-api-reference.md b/docs/api-reference/catalogd-api-reference.md index fa52fbc00d..90ec139048 100644 --- a/docs/api-reference/catalogd-api-reference.md +++ b/docs/api-reference/catalogd-api-reference.md @@ -116,10 +116,26 @@ _Appears in:_ | --- | --- | --- | --- | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterCatalog.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human-readable message that further elaborates on the state of the condition.

The current set of condition types are:
- "Serving", which represents whether or not the contents of the catalog are being served via the HTTP(S) web server.
- "Progressing", which represents whether or not the ClusterCatalog is progressing towards a new state.

The current set of reasons are:
- "Succeeded", this reason is set on the "Progressing" condition when progressing to a new state is successful.
- "Blocked", this reason is set on the "Progressing" condition when the ClusterCatalog controller has encountered an error that requires manual intervention for recovery.
- "Retrying", this reason is set on the "Progressing" condition when the ClusterCatalog controller has encountered an error that might be resolvable on subsequent reconciliation attempts.
- "Available", this reason is set on the "Serving" condition when the contents of the ClusterCatalog are being served via an endpoint on the HTTP(S) web server.
- "Unavailable", this reason is set on the "Serving" condition when there is not an endpoint on the HTTP(S) web server that is serving the contents of the ClusterCatalog. | | | | `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source based on the source type.

Below is an example of a resolved source for an image source:
resolvedSource:

image:
lastSuccessfulPollAttempt: "2024-09-10T12:22:13Z"
ref: quay.io/operatorhubio/catalog@sha256:c7392b4be033da629f9d665fec30f6901de51ce3adebeff0af579f311ee5cf1b
type: Image | | | -| `contentURL` _string_ | contentURL is a cluster-internal URL from which on-cluster components
can read the content of a catalog | | | +| `urls` _[ClusterCatalogURLs](#clustercatalogurls)_ | urls contains the URLs that can be used to access the catalog. | | | | `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastUnpacked represents the time when the
ClusterCatalog object was last unpacked successfully. | | | +#### ClusterCatalogURLs + + + +ClusterCatalogURLs contains the URLs that can be used to access the catalog. + + + +_Appears in:_ +- [ClusterCatalogStatus](#clustercatalogstatus) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `base` _string_ | base is a required cluster-internal URL which provides API access for this ClusterCatalog.
A suffix API access path can be added to retrieve catalog data for the ClusterCatalog.
Currently, a 'v1' API access provides complete FBC retrival via the path "/api/v1/all", with the general form `\{base\}/api/v1/all`. | | Required: \{\}
| + + #### ImageSource diff --git a/docs/api-reference/catalogd-webserver.md b/docs/api-reference/catalogd-webserver.md index a95772eadc..3aed2f6bd6 100644 --- a/docs/api-reference/catalogd-webserver.md +++ b/docs/api-reference/catalogd-webserver.md @@ -3,18 +3,21 @@ [Catalogd](https://github.com/operator-framework/catalogd), the OLM v1 component for making catalog contents available on cluster, includes a web server that serves catalog contents to clients via an HTTP(S) endpoint. -The endpoint to retrieve this information is provided in the `.status.contentURL` of a `ClusterCatalog` resource. -As an example: +The endpoint to retrieve this information can be composed from the `.status.urls.base` of a `ClusterCatalog` resource with the selected access API path. +As an example, to access the full FBC via the v1 API endpoint (indicated by path `api/v1/all`) where `.status.urls.base` is ```yaml - contentURL: https://catalogd-service.olmv1-system.svc/catalogs/operatorhubio/all.json + urls: + base: https://catalogd-service.olmv1-system.svc/catalogs/operatorhubio ``` +the URL to access the service would be `https://catalogd-service.olmv1-system.svc/catalogs/operatorhubio/api/v1/all` + !!! note - The value of the `.status.contentURL` field in a `ClusterCatalog` resource is an arbitrary string value and can change at any time. - While there are no guarantees on the exact value of this field, it will always be a URL that resolves successfully for clients using - it to make a request from within the cluster. + The values of the `.status.urls` field in a `ClusterCatalog` resource are arbitrary string values and can change at any time. + While there are no guarantees on the exact value of this field, it will always contain catalog-specific API endpoints for use + by clients to make a request from within the cluster. ## Interacting With the Server diff --git a/docs/howto/catalog-queries.md b/docs/howto/catalog-queries.md index 4e6f260d79..7eadb85019 100644 --- a/docs/howto/catalog-queries.md +++ b/docs/howto/catalog-queries.md @@ -7,7 +7,7 @@ The following examples will show this default behavior, but for simplicity's sak You can use the `curl` command with `jq` to query catalogs that are installed on your cluster. ``` terminal title="Query syntax" -curl -k https://localhost:8443/catalogs/operatorhubio/all.json | +curl -k https://localhost:8443/catalogs/operatorhubio/api/v1/all | ``` ## Package queries diff --git a/docs/tutorials/add-catalog.md b/docs/tutorials/add-catalog.md index ad3ae06bbd..2b75c666d7 100644 --- a/docs/tutorials/add-catalog.md +++ b/docs/tutorials/add-catalog.md @@ -126,7 +126,8 @@ This catalog is distributed as an image [quay.io/operatorhubio/catalog](https:// Reason: Available Status: True Type: Serving - Content URL: https://catalogd-server.olmv1-system.svc/catalogs/operatorhubio/all.json + URLs: + Base: https://catalogd-server.olmv1-system.svc/catalogs/operatorhubio Last Unpacked: 2024-03-12T19:35:34Z Resolved Source: Image: diff --git a/docs/tutorials/explore-available-content.md b/docs/tutorials/explore-available-content.md index 67ea2127a2..76bae2b6fd 100644 --- a/docs/tutorials/explore-available-content.md +++ b/docs/tutorials/explore-available-content.md @@ -24,9 +24,9 @@ The following examples will show this default behavior, but for simplicity's sak kubectl -n olmv1-system port-forward svc/catalogd-service 8443:443 ``` -2. Return a list of all the extensions in a catalog: +2. Return a list of all the extensions in a catalog via the v1 API endpoint: ``` terminal - curl -k https://localhost:8443/catalogs/operatorhubio/all.json | jq -s '.[] | select(.schema == "olm.package") | .name' + curl -k https://localhost:8443/catalogs/operatorhubio/api/v1/all | jq -s '.[] | select(.schema == "olm.package") | .name' ``` ??? success @@ -96,7 +96,7 @@ The following examples will show this default behavior, but for simplicity's sak * Return list of packages that support `AllNamespaces` install mode and do not use webhooks: ``` terminal - curl -k https://localhost:8443/catalogs/operatorhubio/all.json | jq -c 'select(.schema == "olm.bundle") | {"package":.package, "version":.properties[] | select(.type == "olm.bundle.object").value.data | @base64d | fromjson | select(.kind == "ClusterServiceVersion" and (.spec.installModes[] | select(.type == "AllNamespaces" and .supported == true) != null) and .spec.webhookdefinitions == null).spec.version}' + curl -k https://localhost:8443/catalogs/operatorhubio/api/v1/all | jq -c 'select(.schema == "olm.bundle") | {"package":.package, "version":.properties[] | select(.type == "olm.bundle.object").value.data | @base64d | fromjson | select(.kind == "ClusterServiceVersion" and (.spec.installModes[] | select(.type == "AllNamespaces" and .supported == true) != null) and .spec.webhookdefinitions == null).spec.version}' ``` ??? success @@ -127,7 +127,7 @@ The following examples will show this default behavior, but for simplicity's sak 3. Inspect the contents of an extension's metadata: ``` terminal - curl -k https://localhost:8443/catalogs/operatorhubio/all.json | jq -s '.[] | select( .schema == "olm.package") | select( .name == "")' + curl -k https://localhost:8443/catalogs/operatorhubio/api/v1/all | jq -s '.[] | select( .schema == "olm.package") | select( .name == "")' ``` `package_name` diff --git a/go.mod b/go.mod index ebd0d93b88..300498b3c3 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.33.0 + github.com/operator-framework/catalogd v0.34.0 github.com/operator-framework/helm-operator-plugins v0.5.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index ae31449635..75efd1bdc0 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.33.0 h1:hnLIFykO1FkjOAUFRPuYRIHQTE0oBF9jkGmWjKhxniQ= -github.com/operator-framework/catalogd v0.33.0/go.mod h1:anZurjcFMBvbkuyqlJ98v9z+yjniPKqmhlyitk9DuBQ= +github.com/operator-framework/catalogd v0.34.0 h1:zzCUTVrRJ4oCnjvAdairEWbUejLUnYRVsvAyhfREpyU= +github.com/operator-framework/catalogd v0.34.0/go.mod h1:anZurjcFMBvbkuyqlJ98v9z+yjniPKqmhlyitk9DuBQ= github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= diff --git a/hack/tools/catalogs/download-catalog b/hack/tools/catalogs/download-catalog index 43877690eb..437cb8a755 100755 --- a/hack/tools/catalogs/download-catalog +++ b/hack/tools/catalogs/download-catalog @@ -53,12 +53,14 @@ if [ "$UNPACKED_CONDITION" != "True" ]; then exit 1 fi -# Get the contentURL -CONTENT_URL=$(echo "$CLUSTER_CATALOG" | jq -r '.status.contentURL') -if [ -z "$CONTENT_URL" ]; then - echo "Content URL not found for ClusterCatalog $CATALOG_NAME." +# Construct the API URL from the ClusterCatalog status +CATALOG_API_URL=$(echo "$CLUSTER_CATALOG" | jq -r '.status.urls.base') +if [ -z "$CATALOG_API_URL" ]; then + echo "ClusterCatalog $CATALOG_NAME does not express API endpoint in '.status.urls.base'." exit 1 fi +# Assemble the v1 API URL from the base endpoint +CATALOG_CONTENT_URL="${CATALOG_API_URL}/api/v1/all" # Start port forwarding echo "Starting kubectl port-forward to $CATALOGD_SERVICE_NAME on port $CATALOGD_LOCAL_SERVICE_PORT..." @@ -77,9 +79,9 @@ while ! curl -s "http://localhost:${CATALOGD_LOCAL_SERVICE_PORT}" >/dev/null; do sleep 1 done -# Modify the contentURL to hit localhost: -LOCAL_CONTENT_URL=${CONTENT_URL//https:\/\/$CATALOGD_SERVICE_NAME.$CATALOGD_CATALOGD_SERVICE_NAMESPACE.svc/https:\/\/localhost:$CATALOGD_LOCAL_SERVICE_PORT} -echo "Found content URL: $CONTENT_URL" +# Modify the catalogd service endpoint URL to hit localhost: +LOCAL_CONTENT_URL=${CATALOG_CONTENT_URL//https:\/\/$CATALOGD_SERVICE_NAME.$CATALOGD_CATALOGD_SERVICE_NAMESPACE.svc/https:\/\/localhost:$CATALOGD_LOCAL_SERVICE_PORT} +echo "Calculated catalogd API pathcontent URL: $CATALOG_CONTENT_URL" echo "Using local port: $CATALOGD_LOCAL_SERVICE_PORT" echo "Using local content URL: $LOCAL_CONTENT_URL" diff --git a/internal/catalogmetadata/client/client.go b/internal/catalogmetadata/client/client.go index 43b9720954..fee3b1cc21 100644 --- a/internal/catalogmetadata/client/client.go +++ b/internal/catalogmetadata/client/client.go @@ -7,6 +7,7 @@ import ( "io" "io/fs" "net/http" + "net/url" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -15,6 +16,10 @@ import ( "github.com/operator-framework/operator-registry/alpha/declcfg" ) +const ( + clusterCatalogV1ApiURL = "api/v1/all" +) + type Cache interface { // Get returns cache for a specified catalog name and version (resolvedRef). // @@ -113,7 +118,16 @@ func (c *Client) PopulateCache(ctx context.Context, catalog *catalogd.ClusterCat } func (c *Client) doRequest(ctx context.Context, catalog *catalogd.ClusterCatalog) (*http.Response, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, catalog.Status.ContentURL, nil) + if catalog.Status.URLs == nil { + return nil, fmt.Errorf("error: catalog %q has a nil status.urls value", catalog.Name) + } + + catalogdURL, err := url.JoinPath(catalog.Status.URLs.Base, clusterCatalogV1ApiURL) + if err != nil { + return nil, fmt.Errorf("error forming catalogd API endpoint: %v", err) + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, catalogdURL, nil) if err != nil { return nil, fmt.Errorf("error forming request: %v", err) } diff --git a/internal/catalogmetadata/client/client_test.go b/internal/catalogmetadata/client/client_test.go index 92645bdbfd..33f43c1cf3 100644 --- a/internal/catalogmetadata/client/client_test.go +++ b/internal/catalogmetadata/client/client_test.go @@ -28,7 +28,9 @@ func defaultCatalog() *catalogd.ClusterCatalog { ResolvedSource: &catalogd.ResolvedCatalogSource{Image: &catalogd.ResolvedImageSource{ Ref: "fake/catalog@sha256:fakesha", }}, - ContentURL: "https://fake-url.svc.local/all.json", + URLs: &catalogd.ClusterCatalogURLs{ + Base: "https://fake-url.svc.local/catalogs/catalog-1", + }, }, } } From 31bafb4554b991dd7823739ae43fabf7c270cc55 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Fri, 18 Oct 2024 17:33:17 -0400 Subject: [PATCH 081/694] bump to catalogd v0.35.0 (#1397) Signed-off-by: Joe Lanford --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 300498b3c3..b50546a9df 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.34.0 + github.com/operator-framework/catalogd v0.35.0 github.com/operator-framework/helm-operator-plugins v0.5.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index 75efd1bdc0..9ef9920b98 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.34.0 h1:zzCUTVrRJ4oCnjvAdairEWbUejLUnYRVsvAyhfREpyU= -github.com/operator-framework/catalogd v0.34.0/go.mod h1:anZurjcFMBvbkuyqlJ98v9z+yjniPKqmhlyitk9DuBQ= +github.com/operator-framework/catalogd v0.35.0 h1:2lQPyHIzEfcciUjQ/fo4i/GE/sX2LBzd8S+nYxlvEHU= +github.com/operator-framework/catalogd v0.35.0/go.mod h1:anZurjcFMBvbkuyqlJ98v9z+yjniPKqmhlyitk9DuBQ= github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= From b60477e881e709ddf58d27dd6c3887e117674902 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Mon, 21 Oct 2024 15:51:47 -0400 Subject: [PATCH 082/694] Make sure cert-manager is ready (#1401) There have been a number of test and CI failures due to cert-manager not being ready. This adds additional checks to the deployments and specifically to the mutating webhook configuration. Signed-off-by: Todd Short --- scripts/install.tpl.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/install.tpl.sh b/scripts/install.tpl.sh index e8c857266c..63acdc83c2 100644 --- a/scripts/install.tpl.sh +++ b/scripts/install.tpl.sh @@ -42,7 +42,12 @@ function kubectl_wait_rollout() { } kubectl apply -f "https://github.com/cert-manager/cert-manager/releases/download/$%7Bcert_mgr_version%7D/cert-manager.yaml" +# Wait for cert-manager to be fully ready kubectl_wait "cert-manager" "deployment/cert-manager-webhook" "60s" +kubectl_wait "cert-manager" "deployment/cert-manager-cainjector" "60s" +kubectl_wait "cert-manager" "deployment/cert-manager" "60s" +kubectl wait mutatingwebhookconfigurations/cert-manager-webhook --for=jsonpath='{.webhooks[0].clientConfig.caBundle}' --timeout=60s +kubectl wait validatingwebhookconfigurations/cert-manager-webhook --for=jsonpath='{.webhooks[0].clientConfig.caBundle}' --timeout=60s kubectl apply -f "https://github.com/operator-framework/catalogd/releases/download/$%7Bcatalogd_version%7D/catalogd.yaml" # Wait for the rollout, and then wait for the deployment to be Available From 6f422740ceb64853e72450a5f5a0d4a136444b85 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Tue, 22 Oct 2024 08:23:48 -0400 Subject: [PATCH 083/694] tilt ci: use Go version from go.mod (#1402) Signed-off-by: Joe Lanford --- .github/workflows/tilt.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tilt.yaml b/.github/workflows/tilt.yaml index e8f2d3dc04..3a25fdb73f 100644 --- a/.github/workflows/tilt.yaml +++ b/.github/workflows/tilt.yaml @@ -32,6 +32,10 @@ jobs: repository: operator-framework/catalogd path: catalogd ref: "${{ steps.get-catalogd-version.outputs.CATALOGD_VERSION }}" + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: "operator-controller/go.mod" - name: Install Tilt run: | TILT_VERSION="0.33.3" From 1cb254ce62b3d621d2d1dbf45b7be1611c32e455 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Wed, 23 Oct 2024 15:22:40 -0400 Subject: [PATCH 084/694] Use Helm List operator to determine Deployed status (#1379) Signed-off-by: Todd Short --- go.mod | 2 +- go.sum | 4 +- internal/action/helm.go | 6 + internal/action/helm_test.go | 16 ++ .../clusterextension_controller.go | 83 ++++++---- .../clusterextension_controller_test.go | 145 +++++++++++++++++- internal/controllers/common_controller.go | 28 ++++ internal/controllers/suite_test.go | 6 +- internal/labels/labels.go | 11 +- test/e2e/cluster_extension_install_test.go | 2 +- 10 files changed, 256 insertions(+), 47 deletions(-) diff --git a/go.mod b/go.mod index b50546a9df..a43fddbc76 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 github.com/operator-framework/catalogd v0.35.0 - github.com/operator-framework/helm-operator-plugins v0.5.0 + github.com/operator-framework/helm-operator-plugins v0.7.0 github.com/operator-framework/operator-registry v1.47.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 diff --git a/go.sum b/go.sum index 9ef9920b98..d54f5fb443 100644 --- a/go.sum +++ b/go.sum @@ -537,8 +537,8 @@ github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/ github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= github.com/operator-framework/catalogd v0.35.0 h1:2lQPyHIzEfcciUjQ/fo4i/GE/sX2LBzd8S+nYxlvEHU= github.com/operator-framework/catalogd v0.35.0/go.mod h1:anZurjcFMBvbkuyqlJ98v9z+yjniPKqmhlyitk9DuBQ= -github.com/operator-framework/helm-operator-plugins v0.5.0 h1:qph2OoECcI9mpuUBtOsWOMgvpx52mPTTSvzVxICsT04= -github.com/operator-framework/helm-operator-plugins v0.5.0/go.mod h1:yVncrZ/FJNqedMil+055fk6sw8aMKRrget/AqGM0ig0= +github.com/operator-framework/helm-operator-plugins v0.7.0 h1:YmtIWFc9BaNaDc5mk/dkG0P2BqPZOqpDvjWih5Fczuk= +github.com/operator-framework/helm-operator-plugins v0.7.0/go.mod h1:fUUCJR3bWtMBZ1qdDhbwjacsBHi9uT576tF4u/DwOgQ= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= github.com/operator-framework/operator-lib v0.15.0/go.mod h1:ZxLvFuQ7bRWiTNBOqodbuNvcsy/Iq0kOygdxhlbNdI0= github.com/operator-framework/operator-registry v1.47.0 h1:Imr7X/W6FmXczwpIOXfnX8d6Snr1dzwWxkMG+lLAfhg= diff --git a/internal/action/helm.go b/internal/action/helm.go index 0ac18cf794..d54c469da5 100644 --- a/internal/action/helm.go +++ b/internal/action/helm.go @@ -75,6 +75,12 @@ func (a ActionClient) Get(name string, opts ...actionclient.GetOption) (*release return resp, err } +func (a ActionClient) History(name string, opts ...actionclient.HistoryOption) ([]*release.Release, error) { + resp, err := a.ActionInterface.History(name, opts...) + err = a.actionClientErrorTranslator(err) + return resp, err +} + func (a ActionClient) Reconcile(rel *release.Release) error { return a.actionClientErrorTranslator(a.ActionInterface.Reconcile(rel)) } diff --git a/internal/action/helm_test.go b/internal/action/helm_test.go index d07dc53bcd..3f1f02f9db 100644 --- a/internal/action/helm_test.go +++ b/internal/action/helm_test.go @@ -30,6 +30,17 @@ func (m *mockActionClient) Get(name string, opts ...actionclient.GetOption) (*re return args.Get(0).(*release.Release), args.Error(1) } +func (m *mockActionClient) History(name string, opts ...actionclient.HistoryOption) ([]*release.Release, error) { + args := m.Called(name, opts) + if args.Get(0) == nil { + return nil, args.Error(1) + } + rel := []*release.Release{ + args.Get(0).(*release.Release), + } + return rel, args.Error(1) +} + func (m *mockActionClient) Install(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...actionclient.InstallOption) (*release.Release, error) { args := m.Called(name, namespace, chrt, vals, opts) if args.Get(0) == nil { @@ -82,6 +93,7 @@ func TestActionClientErrorTranslation(t *testing.T) { ac := new(mockActionClient) ac.On("Get", mock.Anything, mock.Anything).Return(nil, originalError) + ac.On("History", mock.Anything, mock.Anything).Return(nil, originalError) ac.On("Install", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, originalError) ac.On("Uninstall", mock.Anything, mock.Anything).Return(nil, originalError) ac.On("Upgrade", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, originalError) @@ -93,6 +105,10 @@ func TestActionClientErrorTranslation(t *testing.T) { _, err := wrappedAc.Get("something") assert.Equal(t, expectedErr, err, "expected Get() to return translated error") + // History + _, err = wrappedAc.History("something") + assert.Equal(t, expectedErr, err, "expected History() to return translated error") + // Install _, err = wrappedAc.Install("something", "somethingelse", nil, nil) assert.Equal(t, expectedErr, err, "expected Install() to return translated error") diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index bb3a1494b7..df54fdcdb6 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -84,7 +84,7 @@ type Applier interface { } type InstalledBundleGetter interface { - GetInstalledBundle(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (*ocv1alpha1.BundleMetadata, error) + GetInstalledBundle(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (*InstalledBundle, error) } //+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensions,verbs=get;list;watch;update;patch @@ -206,19 +206,22 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp installedBundle, err := r.InstalledBundleGetter.GetInstalledBundle(ctx, ext) if err != nil { setInstallStatus(ext, nil) - // TODO: use Installed=Unknown - setInstalledStatusConditionFailed(ext, err.Error()) - setStatusProgressing(ext, err) + setInstalledStatusConditionUnknown(ext, err.Error()) + setStatusProgressing(ext, errors.New("retrying to get installed bundle")) return ctrl.Result{}, err } // run resolution l.Info("resolving bundle") - resolvedBundle, resolvedBundleVersion, resolvedDeprecation, err := r.Resolver.Resolve(ctx, ext, installedBundle) + var bm *ocv1alpha1.BundleMetadata + if installedBundle != nil { + bm = &installedBundle.BundleMetadata + } + resolvedBundle, resolvedBundleVersion, resolvedDeprecation, err := r.Resolver.Resolve(ctx, ext, bm) if err != nil { // Note: We don't distinguish between resolution-specific errors and generic errors - setInstallStatus(ext, nil) setStatusProgressing(ext, err) + setInstalledStatusFromBundle(ext, installedBundle) ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, err.Error()) return ctrl.Result{}, err } @@ -255,6 +258,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp // installed since we intend for the progressing condition to replace the resolved condition // and will be removing the .status.resolution field from the ClusterExtension status API setStatusProgressing(ext, wrapErrorWithResolutionInfo(resolvedBundleMetadata, err)) + setInstalledStatusFromBundle(ext, installedBundle) return ctrl.Result{}, err } @@ -268,9 +272,10 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp } storeLbls := map[string]string{ - labels.BundleNameKey: resolvedBundle.Name, - labels.PackageNameKey: resolvedBundle.Package, - labels.BundleVersionKey: resolvedBundleVersion.String(), + labels.BundleNameKey: resolvedBundle.Name, + labels.PackageNameKey: resolvedBundle.Package, + labels.BundleVersionKey: resolvedBundleVersion.String(), + labels.BundleReferenceKey: resolvedBundle.Image, } l.Info("applying bundle contents") @@ -286,18 +291,17 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp managedObjs, _, err := r.Applier.Apply(ctx, unpackResult.Bundle, ext, objLbls, storeLbls) if err != nil { setStatusProgressing(ext, wrapErrorWithResolutionInfo(resolvedBundleMetadata, err)) - // If bundle is not already installed, set Installed status condition to False - if installedBundle == nil { - setInstalledStatusConditionFailed(ext, err.Error()) - } + // Now that we're actually trying to install, use the error + setInstalledStatusFromBundle(ext, installedBundle) return ctrl.Result{}, err } - installStatus := &ocv1alpha1.ClusterExtensionInstallStatus{ - Bundle: resolvedBundleMetadata, + newInstalledBundle := &InstalledBundle{ + BundleMetadata: resolvedBundleMetadata, + Image: resolvedBundle.Image, } - setInstallStatus(ext, installStatus) - setInstalledStatusConditionSuccess(ext, fmt.Sprintf("Installed bundle %s successfully", resolvedBundle.Image)) + // Successful install + setInstalledStatusFromBundle(ext, newInstalledBundle) l.Info("watching managed objects") cache, err := r.Manager.Get(ctx, ext) @@ -466,32 +470,45 @@ type DefaultInstalledBundleGetter struct { helmclient.ActionClientGetter } -func (d *DefaultInstalledBundleGetter) GetInstalledBundle(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (*ocv1alpha1.BundleMetadata, error) { +type InstalledBundle struct { + ocv1alpha1.BundleMetadata + Image string +} + +func (d *DefaultInstalledBundleGetter) GetInstalledBundle(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (*InstalledBundle, error) { cl, err := d.ActionClientFor(ctx, ext) if err != nil { return nil, err } - rel, err := cl.Get(ext.GetName()) + relhis, err := cl.History(ext.GetName()) if err != nil && !errors.Is(err, driver.ErrReleaseNotFound) { return nil, err } - if rel == nil { + if len(relhis) == 0 { return nil, nil } - switch rel.Info.Status { - case release.StatusUnknown: - return nil, fmt.Errorf("installation status is unknown") - case release.StatusDeployed, release.StatusUninstalled, release.StatusSuperseded, release.StatusFailed: - case release.StatusUninstalling, release.StatusPendingInstall, release.StatusPendingRollback, release.StatusPendingUpgrade: - return nil, fmt.Errorf("installation is still pending: %s", rel.Info.Status) - default: - return nil, fmt.Errorf("unknown installation status: %s", rel.Info.Status) + // relhis[0].Info.Status is the status of the most recent install attempt. + // But we need to look for the most-recent _Deployed_ release + for _, rel := range relhis { + if rel.Info != nil && rel.Info.Status == release.StatusDeployed { + // If there are blank values, we should consider this as not installed + if n, ok := rel.Labels[labels.BundleNameKey]; !ok || n == "" { + return nil, nil + } + if v, ok := rel.Labels[labels.BundleVersionKey]; !ok || v == "" { + return nil, nil + } + // Not checking BundleReferenceKey, as it's new; upgrade test would fail + return &InstalledBundle{ + BundleMetadata: ocv1alpha1.BundleMetadata{ + Name: rel.Labels[labels.BundleNameKey], + Version: rel.Labels[labels.BundleVersionKey], + }, + Image: rel.Labels[labels.BundleReferenceKey], + }, nil + } } - - return &ocv1alpha1.BundleMetadata{ - Name: rel.Labels[labels.BundleNameKey], - Version: rel.Labels[labels.BundleVersionKey], - }, nil + return nil, nil } diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index 8f7331384e..c17390a570 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -12,6 +12,9 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/release" + "helm.sh/helm/v3/pkg/storage/driver" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -21,12 +24,14 @@ import ( crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer" "sigs.k8s.io/controller-runtime/pkg/reconcile" + helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" "github.com/operator-framework/operator-registry/alpha/declcfg" ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/conditionsets" "github.com/operator-framework/operator-controller/internal/controllers" "github.com/operator-framework/operator-controller/internal/finalizers" + "github.com/operator-framework/operator-controller/internal/labels" "github.com/operator-framework/operator-controller/internal/resolve" "github.com/operator-framework/operator-controller/internal/rukpak/source" ) @@ -392,7 +397,10 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { cache: &MockManagedContentCache{}, } reconciler.InstalledBundleGetter = &MockInstalledBundleGetter{ - bundle: &ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + bundle: &controllers.InstalledBundle{ + BundleMetadata: ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + Image: "quay.io/operatorhubio/prometheus@fake1.0.0", + }, } reconciler.Applier = &MockApplier{ objs: []client.Object{}, @@ -745,7 +753,10 @@ func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { cache: &MockManagedContentCache{}, } reconciler.InstalledBundleGetter = &MockInstalledBundleGetter{ - bundle: &ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + bundle: &controllers.InstalledBundle{ + BundleMetadata: ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + Image: "quay.io/operatorhubio/prometheus@fake1.0.0", + }, } err = reconciler.Finalizers.Register(fakeFinalizer, finalizers.FinalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { return crfinalizer.Result{}, errors.New(finalizersMessage) @@ -1400,3 +1411,133 @@ func TestSetDeprecationStatus(t *testing.T) { }) } } + +type MockActionGetter struct { + description string + rels []*release.Release + err error + expectedBundle *controllers.InstalledBundle + expectedError error +} + +func (mag *MockActionGetter) ActionClientFor(ctx context.Context, obj client.Object) (helmclient.ActionInterface, error) { + return mag, nil +} + +func (mag *MockActionGetter) Get(name string, opts ...helmclient.GetOption) (*release.Release, error) { + return nil, nil +} + +// This is the function we are really testing +func (mag *MockActionGetter) History(name string, opts ...helmclient.HistoryOption) ([]*release.Release, error) { + return mag.rels, mag.err +} + +func (mag *MockActionGetter) Install(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...helmclient.InstallOption) (*release.Release, error) { + return nil, nil +} + +func (mag *MockActionGetter) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...helmclient.UpgradeOption) (*release.Release, error) { + return nil, nil +} + +func (mag *MockActionGetter) Uninstall(name string, opts ...helmclient.UninstallOption) (*release.UninstallReleaseResponse, error) { + return nil, nil +} + +func (mag *MockActionGetter) Reconcile(rel *release.Release) error { + return nil +} + +func TestGetInstalledBundleHistory(t *testing.T) { + getter := controllers.DefaultInstalledBundleGetter{} + + ext := ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ext", + }, + } + + mag := []MockActionGetter{ + { + "No return", + nil, nil, + nil, nil, + }, + { + "ErrReleaseNotFound (special case)", + nil, driver.ErrReleaseNotFound, + nil, nil, + }, + { + "Error from History", + nil, fmt.Errorf("generic error"), + nil, fmt.Errorf("generic error"), + }, + { + "One item in history", + []*release.Release{ + { + Name: "test-ext", + Info: &release.Info{ + Status: release.StatusDeployed, + }, + Labels: map[string]string{ + labels.BundleNameKey: "test-ext", + labels.BundleVersionKey: "1.0", + labels.BundleReferenceKey: "bundle-ref", + }, + }, + }, nil, + &controllers.InstalledBundle{ + BundleMetadata: ocv1alpha1.BundleMetadata{ + Name: "test-ext", + Version: "1.0", + }, + Image: "bundle-ref", + }, nil, + }, + { + "Two items in history", + []*release.Release{ + { + Name: "test-ext", + Info: &release.Info{ + Status: release.StatusFailed, + }, + Labels: map[string]string{ + labels.BundleNameKey: "test-ext", + labels.BundleVersionKey: "2.0", + labels.BundleReferenceKey: "bundle-ref-2", + }, + }, + { + Name: "test-ext", + Info: &release.Info{ + Status: release.StatusDeployed, + }, + Labels: map[string]string{ + labels.BundleNameKey: "test-ext", + labels.BundleVersionKey: "1.0", + labels.BundleReferenceKey: "bundle-ref-1", + }, + }, + }, nil, + &controllers.InstalledBundle{ + BundleMetadata: ocv1alpha1.BundleMetadata{ + Name: "test-ext", + Version: "1.0", + }, + Image: "bundle-ref-1", + }, nil, + }, + } + + for _, tst := range mag { + t.Log(tst.description) + getter.ActionClientGetter = &tst + md, err := getter.GetInstalledBundle(context.Background(), &ext) + require.Equal(t, tst.expectedError, err) + require.Equal(t, tst.expectedBundle, md) + } +} diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index d04cd0a01f..cefe539136 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -18,6 +18,7 @@ package controllers import ( "errors" + "fmt" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -26,6 +27,22 @@ import ( ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) +// setInstalledStatusFromBundle sets the installed status based on the given installedBundle. +func setInstalledStatusFromBundle(ext *ocv1alpha1.ClusterExtension, installedBundle *InstalledBundle) { + // Nothing is installed + if installedBundle == nil { + setInstallStatus(ext, nil) + setInstalledStatusConditionFailed(ext, "No bundle installed") + return + } + // Something is installed + installStatus := &ocv1alpha1.ClusterExtensionInstallStatus{ + Bundle: installedBundle.BundleMetadata, + } + setInstallStatus(ext, installStatus) + setInstalledStatusConditionSuccess(ext, fmt.Sprintf("Installed bundle %s successfully", installedBundle.Image)) +} + // setInstalledStatusConditionSuccess sets the installed status condition to success. func setInstalledStatusConditionSuccess(ext *ocv1alpha1.ClusterExtension, message string) { apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ @@ -48,6 +65,17 @@ func setInstalledStatusConditionFailed(ext *ocv1alpha1.ClusterExtension, message }) } +// setInstalledStatusConditionUnknown sets the installed status condition to unknown. +func setInstalledStatusConditionUnknown(ext *ocv1alpha1.ClusterExtension, message string) { + apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ + Type: ocv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: ocv1alpha1.ReasonFailed, + Message: message, + ObservedGeneration: ext.GetGeneration(), + }) +} + func setInstallStatus(ext *ocv1alpha1.ClusterExtension, installStatus *ocv1alpha1.ClusterExtensionInstallStatus) { ext.Status.Install = installStatus } diff --git a/internal/controllers/suite_test.go b/internal/controllers/suite_test.go index 9fa70e4572..97ea3c427d 100644 --- a/internal/controllers/suite_test.go +++ b/internal/controllers/suite_test.go @@ -73,14 +73,14 @@ func newClient(t *testing.T) client.Client { } type MockInstalledBundleGetter struct { - bundle *ocv1alpha1.BundleMetadata + bundle *controllers.InstalledBundle } -func (m *MockInstalledBundleGetter) SetBundle(bundle *ocv1alpha1.BundleMetadata) { +func (m *MockInstalledBundleGetter) SetBundle(bundle *controllers.InstalledBundle) { m.bundle = bundle } -func (m *MockInstalledBundleGetter) GetInstalledBundle(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (*ocv1alpha1.BundleMetadata, error) { +func (m *MockInstalledBundleGetter) GetInstalledBundle(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (*controllers.InstalledBundle, error) { return m.bundle, nil } diff --git a/internal/labels/labels.go b/internal/labels/labels.go index 59061f339a..63bf0c4931 100644 --- a/internal/labels/labels.go +++ b/internal/labels/labels.go @@ -1,9 +1,10 @@ package labels const ( - OwnerKindKey = "olm.operatorframework.io/owner-kind" - OwnerNameKey = "olm.operatorframework.io/owner-name" - PackageNameKey = "olm.operatorframework.io/package-name" - BundleNameKey = "olm.operatorframework.io/bundle-name" - BundleVersionKey = "olm.operatorframework.io/bundle-version" + OwnerKindKey = "olm.operatorframework.io/owner-kind" + OwnerNameKey = "olm.operatorframework.io/owner-name" + PackageNameKey = "olm.operatorframework.io/package-name" + BundleNameKey = "olm.operatorframework.io/bundle-name" + BundleVersionKey = "olm.operatorframework.io/bundle-version" + BundleReferenceKey = "olm.operatorframework.io/bundle-reference" ) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index ed514a70bf..d03aacf5e5 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -769,7 +769,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonFailed, cond.Reason) - assert.Contains(ct, cond.Message, "forbidden") + assert.Equal(ct, "No bundle installed", cond.Message) } }, pollDuration, pollInterval) From f88400caabd8964e63e068e47c0c3f899abe1b6f Mon Sep 17 00:00:00 2001 From: Todd Short Date: Thu, 24 Oct 2024 09:37:50 -0400 Subject: [PATCH 085/694] Remove nil/blank checks for installed bundle labels (#1405) These checks were only needed for the upgrade-e2e Signed-off-by: Todd Short --- internal/controllers/clusterextension_controller.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index df54fdcdb6..76ebd39329 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -493,14 +493,6 @@ func (d *DefaultInstalledBundleGetter) GetInstalledBundle(ctx context.Context, e // But we need to look for the most-recent _Deployed_ release for _, rel := range relhis { if rel.Info != nil && rel.Info.Status == release.StatusDeployed { - // If there are blank values, we should consider this as not installed - if n, ok := rel.Labels[labels.BundleNameKey]; !ok || n == "" { - return nil, nil - } - if v, ok := rel.Labels[labels.BundleVersionKey]; !ok || v == "" { - return nil, nil - } - // Not checking BundleReferenceKey, as it's new; upgrade test would fail return &InstalledBundle{ BundleMetadata: ocv1alpha1.BundleMetadata{ Name: rel.Labels[labels.BundleNameKey], From b9926643c8d855823aa1fb8e176bbd85bd824e2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:50:46 +0200 Subject: [PATCH 086/694] :seedling: Bump the k8s-dependencies group with 2 updates (#1406) Bumps the k8s-dependencies group with 2 updates: [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/cli-runtime` from 0.31.1 to 0.31.2 - [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.31.1...v0.31.2) Updates `k8s.io/client-go` from 0.31.1 to 0.31.2 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.31.1...v0.31.2) --- updated-dependencies: - dependency-name: k8s.io/cli-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-dependencies - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index a43fddbc76..78ae133b52 100644 --- a/go.mod +++ b/go.mod @@ -25,11 +25,11 @@ require ( golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.16.2 - k8s.io/api v0.31.1 + k8s.io/api v0.31.2 k8s.io/apiextensions-apiserver v0.31.1 - k8s.io/apimachinery v0.31.1 - k8s.io/cli-runtime v0.31.1 - k8s.io/client-go v0.31.1 + k8s.io/apimachinery v0.31.2 + k8s.io/cli-runtime v0.31.2 + k8s.io/client-go v0.31.2 k8s.io/component-base v0.31.1 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 diff --git a/go.sum b/go.sum index d54f5fb443..e844fb67fc 100644 --- a/go.sum +++ b/go.sum @@ -989,18 +989,18 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= -k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= +k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= -k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= -k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= -k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk= -k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U= -k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= -k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ= +k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q= +k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= +k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= From 07ed131bcef19fbe1a3439f9252f18f2270c048c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:51:14 +0200 Subject: [PATCH 087/694] :seedling: Bump mkdocs-material from 9.5.41 to 9.5.42 (#1398) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.41 to 9.5.42. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.41...9.5.42) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b44b225bec..1e3e18baf3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ markdown2==2.5.1 MarkupSafe==2.1.5 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.5.41 +mkdocs-material==9.5.42 mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 From dd7898e2b5948d204142ff27bde47b00a6b39294 Mon Sep 17 00:00:00 2001 From: Igor Troyanovsky Date: Mon, 28 Oct 2024 12:31:24 +0200 Subject: [PATCH 088/694] Fix a typo in the catalogs installation step (#1411) --- scripts/install.tpl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install.tpl.sh b/scripts/install.tpl.sh index 63acdc83c2..c1907ddc95 100644 --- a/scripts/install.tpl.sh +++ b/scripts/install.tpl.sh @@ -54,7 +54,7 @@ kubectl apply -f "https://github.com/operator-framework/catalogd/releases/downlo%20kubectl_wait_rollout"olmv1-system" "deployment/catalogd-controller-manager" "60s" kubectl_wait "olmv1-system" "deployment/catalogd-controller-manager" "60s" -if [[ "${install_default_catalogs,,}" != "false" ]]; then +if [[ "${install_default_catalogs}" != "false" ]]; then kubectl apply -f "https://github.com/operator-framework/catalogd/releases/download/$%7Bcatalogd_version%7D/default-catalogs.yaml" kubectl wait --for=condition=Serving "clustercatalog/operatorhubio" --timeout="60s" fi From d1409d9fda4d7529d991fe4d30bf774efd174ba7 Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:58:58 +0100 Subject: [PATCH 089/694] Bump Python to 3.12 and MarkupSafe to 3.0.2 (#1408) Signed-off-by: Mikalai Radchuk --- requirements.txt | 2 +- runtime.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1e3e18baf3..499f7e8b0e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ Jinja2==3.1.4 lxml==5.3.0 Markdown==3.7 markdown2==2.5.1 -MarkupSafe==2.1.5 +MarkupSafe==3.0.2 mergedeep==1.3.4 mkdocs==1.6.1 mkdocs-material==9.5.42 diff --git a/runtime.txt b/runtime.txt index cc1923a40b..24ee5b1be9 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -3.8 +3.13 From 5713012c5522fe328906555cb0f8f3bf894642d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:09:14 +0100 Subject: [PATCH 090/694] :seedling: Bump github.com/operator-framework/operator-registry (#1412) Bumps [github.com/operator-framework/operator-registry](https://github.com/operator-framework/operator-registry) from 1.47.0 to 1.48.0. - [Release notes](https://github.com/operator-framework/operator-registry/releases) - [Commits](https://github.com/operator-framework/operator-registry/compare/v1.47.0...v1.48.0) --- updated-dependencies: - dependency-name: github.com/operator-framework/operator-registry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 40 ++++++++++++++-------------- go.sum | 84 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/go.mod b/go.mod index 78ae133b52..6e614fff73 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/operator-framework/api v0.27.0 github.com/operator-framework/catalogd v0.35.0 github.com/operator-framework/helm-operator-plugins v0.7.0 - github.com/operator-framework/operator-registry v1.47.0 + github.com/operator-framework/operator-registry v1.48.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 @@ -76,9 +76,9 @@ require ( github.com/cyphar/filepath-securejoin v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/cli v27.2.0+incompatible // indirect + github.com/docker/cli v27.3.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v27.1.1+incompatible // indirect + github.com/docker/docker v27.2.0+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -150,7 +150,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/mattn/go-sqlite3 v1.14.23 // indirect + github.com/mattn/go-sqlite3 v1.14.24 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -213,30 +213,30 @@ require ( go.mongodb.org/mongo-driver v1.14.0 // indirect go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/sdk v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.starlark.net v0.0.0-20230612165344-9532f5667272 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/net v0.29.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/tools v0.25.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc v1.66.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index e844fb67fc..078c03cf4c 100644 --- a/go.sum +++ b/go.sum @@ -155,12 +155,12 @@ github.com/distribution/distribution/v3 v3.0.0-beta.1 h1:X+ELTxPuZ1Xe5MsD3kp2wfG github.com/distribution/distribution/v3 v3.0.0-beta.1/go.mod h1:O9O8uamhHzWWQVTjuQpyYUVm/ShPHPUDgvQMpHGVBDs= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v27.2.0+incompatible h1:yHD1QEB1/0vr5eBNpu8tncu8gWxg8EydFPOSKHzXSMM= -github.com/docker/cli v27.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ= +github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= -github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4= +github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -257,8 +257,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4= -github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM= +github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y= +github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -455,8 +455,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0= -github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= +github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= @@ -541,8 +541,8 @@ github.com/operator-framework/helm-operator-plugins v0.7.0 h1:YmtIWFc9BaNaDc5mk/ github.com/operator-framework/helm-operator-plugins v0.7.0/go.mod h1:fUUCJR3bWtMBZ1qdDhbwjacsBHi9uT576tF4u/DwOgQ= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= github.com/operator-framework/operator-lib v0.15.0/go.mod h1:ZxLvFuQ7bRWiTNBOqodbuNvcsy/Iq0kOygdxhlbNdI0= -github.com/operator-framework/operator-registry v1.47.0 h1:Imr7X/W6FmXczwpIOXfnX8d6Snr1dzwWxkMG+lLAfhg= -github.com/operator-framework/operator-registry v1.47.0/go.mod h1:CJ3KcP8uRxtC8l9caM1RsV7r7jYlKAd452tcxcgXyTQ= +github.com/operator-framework/operator-registry v1.48.0 h1:OBTITNJdJuDz+OQVtwHCDP+cAsVeujJH/26HZ6o+zxQ= +github.com/operator-framework/operator-registry v1.48.0/go.mod h1:viEvcrj16nyauX78J38+BEELSaF+uY7GOu6TJdiOSqU= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= @@ -714,16 +714,16 @@ go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 h1:ysCfPZB9AjUlMa1UHYup go.opentelemetry.io/contrib/exporters/autoexport v0.46.1/go.mod h1:ha0aiYm+DOPsLHjh0zoQ8W8sLT+LJ58J3j47lGpSLrU= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM= @@ -734,14 +734,14 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0 h1:dEZWPjVN22urgY go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0/go.mod h1:sTt30Evb7hJB/gEk27qLb1+l9n4Tb8HvHkR0Wx3S6CU= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 h1:VhlEQAPp9R1ktYfrPk5SOryw1e9LDDTZCbIPFrho0ec= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0/go.mod h1:kB3ufRbfU+CQ4MlUcqtW8Z7YEOBeK2DJ6CmR5rYYF3E= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.starlark.net v0.0.0-20230612165344-9532f5667272 h1:2/wtqS591wZyD2OsClsVBKRPEvBsQt/Js+fsCiYhwu8= @@ -763,8 +763,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -810,8 +810,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -855,18 +855,18 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -892,8 +892,8 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= +golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -921,10 +921,10 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 h1:ImUcDPHjTrAqNhlOkSocDLfG9rrNHH7w7uoKWPaWZ8s= google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -932,8 +932,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= -google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -945,8 +945,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 5f8f2026b61f218e03a3ce7836d7fba489a235a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:38:45 +0100 Subject: [PATCH 091/694] :seedling: Bump pymdown-extensions from 10.11.2 to 10.12 (#1415) Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.11.2 to 10.12. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.11.2...10.12) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 499f7e8b0e..5c473d4d36 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 Pygments==2.18.0 -pymdown-extensions==10.11.2 +pymdown-extensions==10.12 pyquery==2.0.1 python-dateutil==2.9.0.post0 PyYAML==6.0.2 From 87dfebe168b20c4abacb9a4f7e406ebca9716ae1 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Wed, 30 Oct 2024 06:09:25 -0400 Subject: [PATCH 092/694] Loosen certificate checks (#1413) The checks we had were a bit too constrained, and don't follow the golang standard library code. So, replace our replacement functions with the originals. This level of checking (e.g. mistyping a path) is not really necessary when people are using our (e.g. cert-manager) manifests. Signed-off-by: Todd Short --- internal/httputil/certutil.go | 196 +--------------------- internal/httputil/certutil_test.go | 15 +- testdata/certs/bad/Amazon_Root_CA_1.pem | 20 --- testdata/certs/bad/Amazon_Root_CA_2.pem | 3 - testdata/certs/bad/Amazon_Root_CA_3.pem | 12 -- testdata/certs/expired/expired.pem | 31 ---- testdata/certs/ugly/Amazon_Root_CA.pem | 37 ---- testdata/certs/ugly2/Amazon_Root_CA_1.pem | 20 --- testdata/certs/ugly3/not_a_cert.pem | 1 - 9 files changed, 5 insertions(+), 330 deletions(-) delete mode 100644 testdata/certs/bad/Amazon_Root_CA_1.pem delete mode 100644 testdata/certs/bad/Amazon_Root_CA_2.pem delete mode 100644 testdata/certs/bad/Amazon_Root_CA_3.pem delete mode 100644 testdata/certs/expired/expired.pem delete mode 100644 testdata/certs/ugly/Amazon_Root_CA.pem delete mode 100644 testdata/certs/ugly2/Amazon_Root_CA_1.pem delete mode 100644 testdata/certs/ugly3/not_a_cert.pem diff --git a/internal/httputil/certutil.go b/internal/httputil/certutil.go index 767fd57a64..a6cd9f98eb 100644 --- a/internal/httputil/certutil.go +++ b/internal/httputil/certutil.go @@ -1,10 +1,7 @@ package httputil import ( - "bytes" "crypto/x509" - "encoding/base64" - "encoding/pem" "fmt" "os" "path/filepath" @@ -13,192 +10,6 @@ import ( "github.com/go-logr/logr" ) -var pemStart = []byte("\n-----BEGIN ") -var pemEnd = []byte("\n-----END ") -var pemEndOfLine = []byte("-----") -var colon = []byte(":") - -// getLine results the first \r\n or \n delineated line from the given byte -// array. The line does not include trailing whitespace or the trailing new -// line bytes. The remainder of the byte array (also not including the new line -// bytes) is also returned and this will always be smaller than the original -// argument. -func getLine(data []byte) ([]byte, []byte) { - i := bytes.IndexByte(data, '\n') - var j int - if i < 0 { - i = len(data) - j = i - } else { - j = i + 1 - if i > 0 && data[i-1] == '\r' { - i-- - } - } - return bytes.TrimRight(data[0:i], " \t"), data[j:] -} - -// removeSpacesAndTabs returns a copy of its input with all spaces and tabs -// removed, if there were any. Otherwise, the input is returned unchanged. -// -// The base64 decoder already skips newline characters, so we don't need to -// filter them out here. -func removeSpacesAndTabs(data []byte) []byte { - if !bytes.ContainsAny(data, " \t") { - // Fast path; most base64 data within PEM contains newlines, but - // no spaces nor tabs. Skip the extra alloc and work. - return data - } - result := make([]byte, len(data)) - n := 0 - - for _, b := range data { - if b == ' ' || b == '\t' { - continue - } - result[n] = b - n++ - } - - return result[0:n] -} - -// This version of pem.Decode() is a bit less flexible, it will not skip over bad PEM -// It is basically the guts of pem.Decode() inside the outer for loop, with error -// returns rather than continues -func pemDecode(data []byte) (*pem.Block, []byte) { - // pemStart begins with a newline. However, at the very beginning of - // the byte array, we'll accept the start string without it. - rest := data - if bytes.HasPrefix(rest, pemStart[1:]) { - rest = rest[len(pemStart)-1:] - } else if _, after, ok := bytes.Cut(rest, pemStart); ok { - rest = after - } else { - return nil, data - } - - var typeLine []byte - typeLine, rest = getLine(rest) - if !bytes.HasSuffix(typeLine, pemEndOfLine) { - return nil, data - } - typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)] - - p := &pem.Block{ - Headers: make(map[string]string), - Type: string(typeLine), - } - - for { - // This loop terminates because getLine's second result is - // always smaller than its argument. - if len(rest) == 0 { - return nil, data - } - line, next := getLine(rest) - - key, val, ok := bytes.Cut(line, colon) - if !ok { - break - } - - key = bytes.TrimSpace(key) - val = bytes.TrimSpace(val) - p.Headers[string(key)] = string(val) - rest = next - } - - var endIndex, endTrailerIndex int - - // If there were no headers, the END line might occur - // immediately, without a leading newline. - if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) { - endIndex = 0 - endTrailerIndex = len(pemEnd) - 1 - } else { - endIndex = bytes.Index(rest, pemEnd) - endTrailerIndex = endIndex + len(pemEnd) - } - - if endIndex < 0 { - return nil, data - } - - // After the "-----" of the ending line, there should be the same type - // and then a final five dashes. - endTrailer := rest[endTrailerIndex:] - endTrailerLen := len(typeLine) + len(pemEndOfLine) - if len(endTrailer) < endTrailerLen { - return nil, data - } - - restOfEndLine := endTrailer[endTrailerLen:] - endTrailer = endTrailer[:endTrailerLen] - if !bytes.HasPrefix(endTrailer, typeLine) || - !bytes.HasSuffix(endTrailer, pemEndOfLine) { - return nil, data - } - - // The line must end with only whitespace. - if s, _ := getLine(restOfEndLine); len(s) != 0 { - return nil, data - } - - base64Data := removeSpacesAndTabs(rest[:endIndex]) - p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) - n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) - if err != nil { - return nil, data - } - p.Bytes = p.Bytes[:n] - - // the -1 is because we might have only matched pemEnd without the - // leading newline if the PEM block was empty. - _, rest = getLine(rest[endIndex+len(pemEnd)-1:]) - return p, rest -} - -// This version of (*x509.CertPool).AppendCertsFromPEM() will error out if parsing fails -func appendCertsFromPEM(s *x509.CertPool, pemCerts []byte, firstExpiration *time.Time) error { - n := 1 - for len(pemCerts) > 0 { - var block *pem.Block - block, pemCerts = pemDecode(pemCerts) - if block == nil { - return fmt.Errorf("unable to PEM decode cert %d", n) - } - // ignore non-certificates (e.g. keys) - if block.Type != "CERTIFICATE" { - continue - } - if len(block.Headers) != 0 { - // This is a cert, but we're ignoring it, so bump the counter - n++ - continue - } - - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return fmt.Errorf("unable to parse cert %d: %w", n, err) - } - if firstExpiration.IsZero() || firstExpiration.After(cert.NotAfter) { - *firstExpiration = cert.NotAfter - } - now := time.Now() - if now.Before(cert.NotBefore) { - return fmt.Errorf("not yet valid cert %d: %q", n, cert.NotBefore.Format(time.RFC3339)) - } else if now.After(cert.NotAfter) { - return fmt.Errorf("expired cert %d: %q", n, cert.NotAfter.Format(time.RFC3339)) - } - // no return values - panics or always succeeds - s.AddCert(cert) - n++ - } - - return nil -} - func NewCertPool(caDir string, log logr.Logger) (*x509.CertPool, error) { caCertPool, err := x509.SystemCertPool() if err != nil { @@ -231,11 +42,10 @@ func NewCertPool(caDir string, log logr.Logger) (*x509.CertPool, error) { if err != nil { return nil, fmt.Errorf("error reading cert file %q: %w", file, err) } - err = appendCertsFromPEM(caCertPool, data, &firstExpiration) - if err != nil { - return nil, fmt.Errorf("error adding cert file %q: %w", file, err) + // The return indicates if any certs were added + if caCertPool.AppendCertsFromPEM(data) { + count++ } - count++ } // Found no certs! diff --git a/internal/httputil/certutil_test.go b/internal/httputil/certutil_test.go index a8a158ff37..f6489ab996 100644 --- a/internal/httputil/certutil_test.go +++ b/internal/httputil/certutil_test.go @@ -11,13 +11,7 @@ import ( ) // The "good" test consists of 3 Amazon Root CAs, along with a "PRIVATE KEY" in one of the files -// The "bad" test consists of 2 Amazon Root CAs, the second of which is garbage, and the test fails -// The "ugly" test consists of a single file: -// - Amazon_Root_CA_1 -// - garbage PEM -// - Amazon_Root_CA_3 -// The error is _not_ detected because the golang standard library PEM decoder skips right over the garbage -// This demonstrates the danger of putting multiple certificates into a single file +// The "empty" test includes a single file with no PEM contents func TestNewCertPool(t *testing.T) { caDirs := []struct { dir string @@ -25,12 +19,7 @@ func TestNewCertPool(t *testing.T) { }{ {"../../testdata/certs/", `no certificates found in "../../testdata/certs/"`}, {"../../testdata/certs/good", ""}, - {"../../testdata/certs/bad", `error adding cert file "../../testdata/certs/bad/Amazon_Root_CA_2.pem": unable to PEM decode cert 1`}, - {"../../testdata/certs/ugly", `error adding cert file "../../testdata/certs/ugly/Amazon_Root_CA.pem": unable to PEM decode cert 2`}, - {"../../testdata/certs/ugly2", `error adding cert file "../../testdata/certs/ugly2/Amazon_Root_CA_1.pem": unable to PEM decode cert 1`}, - {"../../testdata/certs/ugly3", `error adding cert file "../../testdata/certs/ugly3/not_a_cert.pem": unable to PEM decode cert 1`}, - {"../../testdata/certs/empty", `error adding cert file "../../testdata/certs/empty/empty.pem": unable to parse cert 1: x509: malformed certificate`}, - {"../../testdata/certs/expired", `error adding cert file "../../testdata/certs/expired/expired.pem": expired cert 1: "2024-01-02T15:00:00Z"`}, + {"../../testdata/certs/empty", `no certificates found in "../../testdata/certs/empty"`}, } log, _ := logr.FromContext(context.Background()) diff --git a/testdata/certs/bad/Amazon_Root_CA_1.pem b/testdata/certs/bad/Amazon_Root_CA_1.pem deleted file mode 100644 index a6f3e92af5..0000000000 --- a/testdata/certs/bad/Amazon_Root_CA_1.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj -ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM -9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw -IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 -VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L -93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm -jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA -A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI -U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs -N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv -o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU -5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy -rqXRfboQnoZsG4q5WTP468SQvvG5 ------END CERTIFICATE----- diff --git a/testdata/certs/bad/Amazon_Root_CA_2.pem b/testdata/certs/bad/Amazon_Root_CA_2.pem deleted file mode 100644 index 40e771524f..0000000000 --- a/testdata/certs/bad/Amazon_Root_CA_2.pem +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN CERTIFICATE----- -This is badly formatted base64 ------END CERTIFICATE----- diff --git a/testdata/certs/bad/Amazon_Root_CA_3.pem b/testdata/certs/bad/Amazon_Root_CA_3.pem deleted file mode 100644 index a45da7074f..0000000000 --- a/testdata/certs/bad/Amazon_Root_CA_3.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl -ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr -ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr -BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM -YyRIHN8wfdVoOw== ------END CERTIFICATE----- diff --git a/testdata/certs/expired/expired.pem b/testdata/certs/expired/expired.pem deleted file mode 100644 index e8912ba61e..0000000000 --- a/testdata/certs/expired/expired.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFXzCCA0egAwIBAgIUN5r8l1RrpH53+9e6pfj6CXoyqP0wDQYJKoZIhvcNAQEL -BQAwPzELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB1JlZCBIYXQxDDAKBgNVBAsMA09M -TTEQMA4GA1UEAwwHZXhwaXJlZDAeFw0yNDAxMDExNTAwMDBaFw0yNDAxMDIxNTAw -MDBaMD8xCzAJBgNVBAYTAlVTMRAwDgYDVQQKDAdSZWQgSGF0MQwwCgYDVQQLDANP -TE0xEDAOBgNVBAMMB2V4cGlyZWQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQDFalyjEXz0cMGs3pt360Cz0uD0CDnnAqQFHxXPchfCMZnW/VRGrJQq29rZ -UgU5PnxPgqadrw20BodfR2RS9xIacMP+092GY7Ep96xWokwXcsGPj2e5VMlEYVM1 -0MGqIbEv52ZnoEaZDHl4yprYeTs+b/7NGvdG1+N/YNAjkpk8cCBKUXo4ZhkgAZoW -jbv3DkAdkpQHipUYkQZNRws1ebyfTbKaEPxw7abEh9TJrHD1EI9hbmYOGJWLfe1e -zeBQjFioQA31FcQR3/v+aNEDX390+qi3p0LXe7GMabgcoFYcGXO7XvX0DdUBvdZZ -dyHA7cJvyfWfcbucI7xQ9xvAnu/4Ih4D8mHnJXjZK5ReQn06FPM/ZCgZ5LrHAKcZ -0mrOts/8noY9dMmBreSJmLCP8EqzY7yKJFFHVCeKo+bU6/KOyNhJGGSCHVJ/pZGK -ZpOQcNwVvHciLH+MfpW12xJXPEs8Wv24KufDdBCDliSFnVTYH3kZaq4Ozb7+3A5j -wUQ2aDg8nrq4oNORMSCafvia8MYH3NXbpUq1SAyD5DTKtMcWY3gcVnJgrBai1hPn -TPhrMb2NMDFnMnj7/l8jdu9xHrsgOmOrv7Zj0ytmpT6ITJgWNGXsiq7Dp+HH1c6N -ggG6g0zqoyoaxcPVN7PMrWTvfKUD3LHfIsesPc4+lT+TSlBQYwIDAQABo1MwUTAd -BgNVHQ4EFgQU8mBHR/00anEl8Io/A2c0LQlGF5MwHwYDVR0jBBgwFoAU8mBHR/00 -anEl8Io/A2c0LQlGF5MwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC -AgEABZYGEeJY2dgyi4W0LNVgN8mKuZuapIcisQ66foe46WuWGAjVONIHlb0Ciy75 -aaClLC8fiiIh+FUFZ5aIZkfhKH97QvehFO5O7mqCjM7ipvtEm+Vs1IVtXWDONUxo -SfgbjEPBV8+eflgvKQ6jJqiSqs8EnqdbGAfhxVG/3RN1b5xSFtKz6kzHQE+Gy6QT -DGCVhYvDq8j6G2LCePsqE8piOnSaXuRwD4/YEOaYhx4jjgOnaM0m/dM/Cx9wy2xg -LMRBjBwxFf6palgiFUvyqvturIPONQICkM/lZkpmHbeM4FCat/CD5VW+JgpYiEtW -2oFslTEbawUjmEYnzdo9iw9KPLJQqtasFEWzkWWnJrfm7AVGxcgAHVqGZhUMgq0k -MccM2zYZN2fCSZUUueDB7VCxFq5jK2oLzE14ngXdR7ZbxT3qai/zvGg1kl9y1bIF -WVTK0WZnHqZwVnHQVBH0Duv0uyRUzb6yRRziuLN5aBGQpy/Jm7MS0jLidCbqoCXC -dYqGMFlImzU+6CwPyTJo+X+v6L+FATIxZRpBBeEhHqEU6wz51ms68Sjx4bpW33b+ -WFt0JKEmIxB1puJK1qQvKu/MxJyy52GNqiRg7HXkJH9MMYWoAkF2jKMLFoerUPun -7GaV8SIUTFO/5pbnpxZ97a2FuB2RvDKs7GSdspEmC3wbAPU= ------END CERTIFICATE----- diff --git a/testdata/certs/ugly/Amazon_Root_CA.pem b/testdata/certs/ugly/Amazon_Root_CA.pem deleted file mode 100644 index 256719fc7c..0000000000 --- a/testdata/certs/ugly/Amazon_Root_CA.pem +++ /dev/null @@ -1,37 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj -ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM -9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw -IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 -VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L -93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm -jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA -A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI -U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs -N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv -o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU -5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy -rqXRfboQnoZsG4q5WTP468SQvvG5 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF -This is garbage -4PsJYGw= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl -ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr -ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr -BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM -YyRIHN8wfdVoOw== ------END CERTIFICATE----- diff --git a/testdata/certs/ugly2/Amazon_Root_CA_1.pem b/testdata/certs/ugly2/Amazon_Root_CA_1.pem deleted file mode 100644 index 8f0f649728..0000000000 --- a/testdata/certs/ugly2/Amazon_Root_CA_1.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE -MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj -ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM -9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw -IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 -VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L -93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm -jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA -A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI -U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs -N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv -o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU -5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy -rqXRfboQnoZsG4q5WTP468SQvvG5 ------END CERTIFICATE----- diff --git a/testdata/certs/ugly3/not_a_cert.pem b/testdata/certs/ugly3/not_a_cert.pem deleted file mode 100644 index 980a0d5f19..0000000000 --- a/testdata/certs/ugly3/not_a_cert.pem +++ /dev/null @@ -1 +0,0 @@ -Hello World! From 2a7ae996f9cb5bf26a4aabd7eb66f41737e549f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:35:04 -0400 Subject: [PATCH 093/694] :seedling: Bump mkdocs-material from 9.5.42 to 9.5.43 (#1419) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.42 to 9.5.43. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.42...9.5.43) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5c473d4d36..91d817961e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ markdown2==2.5.1 MarkupSafe==3.0.2 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.5.42 +mkdocs-material==9.5.43 mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 From cd0b096468d5d3ca6268dbef22d9b1eea789f771 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:36:28 -0400 Subject: [PATCH 094/694] :seedling: Bump github.com/fsnotify/fsnotify from 1.7.0 to 1.8.0 (#1420) Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.7.0 to 1.8.0. - [Release notes](https://github.com/fsnotify/fsnotify/releases) - [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md) - [Commits](https://github.com/fsnotify/fsnotify/compare/v1.7.0...v1.8.0) --- updated-dependencies: - dependency-name: github.com/fsnotify/fsnotify dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6e614fff73..36b15ebee3 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/blang/semver/v4 v4.0.0 github.com/containerd/containerd v1.7.23 github.com/containers/image/v5 v5.32.2 - github.com/fsnotify/fsnotify v1.7.0 + github.com/fsnotify/fsnotify v1.8.0 github.com/go-logr/logr v1.4.2 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.2 diff --git a/go.sum b/go.sum index 078c03cf4c..bec4549d6d 100644 --- a/go.sum +++ b/go.sum @@ -196,8 +196,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= From a0f12e6b9a10a6130ba7e716c4b282c3f2c061ae Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Fri, 1 Nov 2024 12:45:07 +0100 Subject: [PATCH 095/694] Populate/update cache on ClusterCatalog reconcile (#1284) Currently we fetch catalog data and populate cache on demand during ClusterExtension reconciliation. This works but the first reconciliation after ClusterCatalog creation or update is slow due to the need to fetch data. With this change we proactively populate cache on ClusterCatalog creation and check if cache needs to be updated on ClusterCatalog update. Signed-off-by: Mikalai Radchuk --- cmd/manager/main.go | 5 +- internal/catalogmetadata/client/client.go | 11 +- .../catalogmetadata/client/client_test.go | 19 +- .../controllers/clustercatalog_controller.go | 58 ++++-- .../clustercatalog_controller_test.go | 188 +++++++++++++++--- 5 files changed, 206 insertions(+), 75 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index b03472dfc0..c353a4cb06 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -314,8 +314,9 @@ func main() { } if err = (&controllers.ClusterCatalogReconciler{ - Client: cl, - Cache: catalogClientBackend, + Client: cl, + CatalogCache: catalogClientBackend, + CatalogCachePopulator: catalogClient, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ClusterCatalog") os.Exit(1) diff --git a/internal/catalogmetadata/client/client.go b/internal/catalogmetadata/client/client.go index fee3b1cc21..a68c6c9896 100644 --- a/internal/catalogmetadata/client/client.go +++ b/internal/catalogmetadata/client/client.go @@ -65,17 +65,10 @@ func (c *Client) GetPackage(ctx context.Context, catalog *catalogd.ClusterCatalo catalogFsys, err := c.cache.Get(catalog.Name, catalog.Status.ResolvedSource.Image.Ref) if err != nil { - return nil, fmt.Errorf("error retrieving catalog cache: %v", err) + return nil, fmt.Errorf("error retrieving cache for catalog %q: %v", catalog.Name, err) } if catalogFsys == nil { - // TODO: https://github.com/operator-framework/operator-controller/pull/1284 - // For now we are still populating cache (if absent) on-demand, - // but we might end up just returning a "cache not found" error here - // once we implement cache population in the controller. - catalogFsys, err = c.PopulateCache(ctx, catalog) - if err != nil { - return nil, fmt.Errorf("error fetching catalog contents: %v", err) - } + return nil, fmt.Errorf("cache for catalog %q not found", catalog.Name) } pkgFsys, err := fs.Sub(catalogFsys, pkgName) diff --git a/internal/catalogmetadata/client/client_test.go b/internal/catalogmetadata/client/client_test.go index 33f43c1cf3..15430d7c1f 100644 --- a/internal/catalogmetadata/client/client_test.go +++ b/internal/catalogmetadata/client/client_test.go @@ -62,7 +62,7 @@ func TestClientGetPackage(t *testing.T) { catalog: defaultCatalog, cache: &fakeCache{getErr: errors.New("fetch error")}, assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { - assert.ErrorContains(t, err, `error retrieving catalog cache`) + assert.ErrorContains(t, err, `error retrieving cache for catalog "catalog-1"`) }, }, { @@ -114,18 +114,7 @@ func TestClientGetPackage(t *testing.T) { return testFS, nil }}, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { - require.NoError(t, err) - assert.Equal(t, &declcfg.DeclarativeConfig{Packages: []declcfg.Package{{Schema: declcfg.SchemaPackage, Name: "pkg-present"}}}, fbc) - }, - }, - { - name: "cache unpopulated and fails to populate", - catalog: defaultCatalog, - pkgName: "pkg-present", - cache: &fakeCache{putErr: errors.New("fake cache put error")}, - assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { - assert.Nil(t, fbc) - assert.ErrorContains(t, err, "error fetching catalog contents") + assert.ErrorContains(t, err, `cache for catalog "catalog-1" not found`) }, }, } { @@ -278,7 +267,6 @@ type fakeCache struct { getErr error putFunc func(source string, errToCache error) (fs.FS, error) - putErr error } func (c *fakeCache) Get(catalogName, resolvedRef string) (fs.FS, error) { @@ -293,9 +281,6 @@ func (c *fakeCache) Put(catalogName, resolvedRef string, source io.Reader, errTo } return c.putFunc(buf.String(), errToCache) } - if c.putErr != nil { - return nil, c.putErr - } return nil, errors.New("unexpected error") } diff --git a/internal/controllers/clustercatalog_controller.go b/internal/controllers/clustercatalog_controller.go index 0f7a26a6ca..dc86ed59fa 100644 --- a/internal/controllers/clustercatalog_controller.go +++ b/internal/controllers/clustercatalog_controller.go @@ -18,25 +18,30 @@ package controllers import ( "context" + "fmt" + "io/fs" apierrors "k8s.io/apimachinery/pkg/api/errors" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" ) -type CatalogCacheRemover interface { +type CatalogCache interface { + Get(catalogName, resolvedRef string) (fs.FS, error) Remove(catalogName string) error } +type CatalogCachePopulator interface { + PopulateCache(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) +} + // ClusterCatalogReconciler reconciles a ClusterCatalog object type ClusterCatalogReconciler struct { client.Client - Cache CatalogCacheRemover + CatalogCache CatalogCache + CatalogCachePopulator CatalogCachePopulator } //+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clustercatalogs,verbs=get;list;watch @@ -45,31 +50,44 @@ func (r *ClusterCatalogReconciler) Reconcile(ctx context.Context, req ctrl.Reque existingCatalog := &catalogd.ClusterCatalog{} err := r.Client.Get(ctx, req.NamespacedName, existingCatalog) if apierrors.IsNotFound(err) { - return ctrl.Result{}, r.Cache.Remove(req.Name) + if err := r.CatalogCache.Remove(req.Name); err != nil { + return ctrl.Result{}, fmt.Errorf("error removing cache for catalog %q: %v", req.Name, err) + } + return ctrl.Result{}, nil } if err != nil { return ctrl.Result{}, err } + + if existingCatalog.Status.ResolvedSource == nil || + existingCatalog.Status.ResolvedSource.Image == nil || + existingCatalog.Status.ResolvedSource.Image.Ref == "" { + // Reference is not known yet - skip cache population with no error. + // Once the reference is resolved another reconcile cycle + // will be triggered and we will progress further. + return ctrl.Result{}, nil + } + + catalogFsys, err := r.CatalogCache.Get(existingCatalog.Name, existingCatalog.Status.ResolvedSource.Image.Ref) + if err != nil { + return ctrl.Result{}, fmt.Errorf("error retrieving cache for catalog %q: %v", existingCatalog.Name, err) + } + if catalogFsys != nil { + // Cache already exists so we do not need to populate it + return ctrl.Result{}, nil + } + + if _, err = r.CatalogCachePopulator.PopulateCache(ctx, existingCatalog); err != nil { + return ctrl.Result{}, fmt.Errorf("error populating cache for catalog %q: %v", existingCatalog.Name, err) + } + return ctrl.Result{}, nil } // SetupWithManager sets up the controller with the Manager. func (r *ClusterCatalogReconciler) SetupWithManager(mgr ctrl.Manager) error { _, err := ctrl.NewControllerManagedBy(mgr). - For(&catalogd.ClusterCatalog{}, builder.WithPredicates(predicate.Funcs{ - CreateFunc: func(e event.CreateEvent) bool { - return false - }, - UpdateFunc: func(e event.UpdateEvent) bool { - return false - }, - DeleteFunc: func(e event.DeleteEvent) bool { - return true - }, - GenericFunc: func(e event.GenericEvent) bool { - return false - }, - })). + For(&catalogd.ClusterCatalog{}). Build(r) return err diff --git a/internal/controllers/clustercatalog_controller_test.go b/internal/controllers/clustercatalog_controller_test.go index 762fa15eca..639f20c963 100644 --- a/internal/controllers/clustercatalog_controller_test.go +++ b/internal/controllers/clustercatalog_controller_test.go @@ -3,7 +3,9 @@ package controllers_test import ( "context" "errors" + "io/fs" "testing" + "testing/fstest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -19,38 +21,142 @@ import ( ) func TestClusterCatalogReconcilerFinalizers(t *testing.T) { + const fakeResolvedRef = "fake/catalog@sha256:fakesha1" catalogKey := types.NamespacedName{Name: "test-catalog"} for _, tt := range []struct { - name string - catalog *catalogd.ClusterCatalog - cacheRemoveFunc func(catalogName string) error - wantCacheRemoveCalled bool - wantErr string + name string + catalog *catalogd.ClusterCatalog + catalogCache mockCatalogCache + catalogCachePopulator mockCatalogCachePopulator + wantGetCacheCalled bool + wantRemoveCacheCalled bool + wantPopulateCacheCalled bool + wantErr string }{ { - name: "catalog exists", + name: "catalog exists - cache unpopulated", catalog: &catalogd.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: catalogKey.Name, }, + Status: catalogd.ClusterCatalogStatus{ + ResolvedSource: &catalogd.ResolvedCatalogSource{ + Image: &catalogd.ResolvedImageSource{ + Ref: fakeResolvedRef, + }, + }, + }, + }, + catalogCachePopulator: mockCatalogCachePopulator{ + populateCacheFunc: func(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalog.Name) + return nil, nil + }, + }, + wantGetCacheCalled: true, + wantPopulateCacheCalled: true, + }, + { + name: "catalog exists - cache already populated", + catalog: &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: catalogKey.Name, + }, + Status: catalogd.ClusterCatalogStatus{ + ResolvedSource: &catalogd.ResolvedCatalogSource{ + Image: &catalogd.ResolvedImageSource{ + Ref: fakeResolvedRef, + }, + }, + }, + }, + catalogCache: mockCatalogCache{ + getFunc: func(catalogName, resolvedRef string) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalogName) + assert.Equal(t, fakeResolvedRef, resolvedRef) + // Just any non-nil fs.FS to simulate existence of cache + return fstest.MapFS{}, nil + }, + }, + wantGetCacheCalled: true, + }, + { + name: "catalog exists - catalog not yet resolved", + catalog: &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: catalogKey.Name, + }, + }, + }, + { + name: "catalog exists - error on cache population", + catalog: &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: catalogKey.Name, + }, + Status: catalogd.ClusterCatalogStatus{ + ResolvedSource: &catalogd.ResolvedCatalogSource{ + Image: &catalogd.ResolvedImageSource{ + Ref: fakeResolvedRef, + }, + }, + }, }, + catalogCachePopulator: mockCatalogCachePopulator{ + populateCacheFunc: func(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalog.Name) + return nil, errors.New("fake error from populate cache function") + }, + }, + wantGetCacheCalled: true, + wantPopulateCacheCalled: true, + wantErr: "error populating cache for catalog", + }, + { + name: "catalog exists - error on cache get", + catalog: &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: catalogKey.Name, + }, + Status: catalogd.ClusterCatalogStatus{ + ResolvedSource: &catalogd.ResolvedCatalogSource{ + Image: &catalogd.ResolvedImageSource{ + Ref: fakeResolvedRef, + }, + }, + }, + }, + catalogCache: mockCatalogCache{ + getFunc: func(catalogName, resolvedRef string) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalogName) + assert.Equal(t, fakeResolvedRef, resolvedRef) + return nil, errors.New("fake error from cache get function") + }, + }, + wantGetCacheCalled: true, + wantErr: "error retrieving cache for catalog", }, { name: "catalog does not exist", - cacheRemoveFunc: func(catalogName string) error { - assert.Equal(t, catalogKey.Name, catalogName) - return nil + catalogCache: mockCatalogCache{ + removeFunc: func(catalogName string) error { + assert.Equal(t, catalogKey.Name, catalogName) + return nil + }, }, - wantCacheRemoveCalled: true, + wantRemoveCacheCalled: true, }, { name: "catalog does not exist - error on removal", - cacheRemoveFunc: func(catalogName string) error { - return errors.New("fake error from remove") + catalogCache: mockCatalogCache{ + removeFunc: func(catalogName string) error { + assert.Equal(t, catalogKey.Name, catalogName) + return errors.New("fake error from remove") + }, }, - wantCacheRemoveCalled: true, - wantErr: "fake error from remove", + wantRemoveCacheCalled: true, + wantErr: "error removing cache for catalog", }, } { t.Run(tt.name, func(t *testing.T) { @@ -62,13 +168,10 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { } cl := clientBuilder.Build() - cacheRemover := &mockCatalogCacheRemover{ - removeFunc: tt.cacheRemoveFunc, - } - reconciler := &controllers.ClusterCatalogReconciler{ - Client: cl, - Cache: cacheRemover, + Client: cl, + CatalogCache: controllers.CatalogCache(&tt.catalogCache), + CatalogCachePopulator: controllers.CatalogCachePopulator(&tt.catalogCachePopulator), } result, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catalogKey}) @@ -79,17 +182,48 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { } require.Equal(t, ctrl.Result{}, result) - assert.Equal(t, tt.wantCacheRemoveCalled, cacheRemover.called) + assert.Equal(t, tt.wantRemoveCacheCalled, tt.catalogCache.removeFuncCalled) + assert.Equal(t, tt.wantGetCacheCalled, tt.catalogCache.getFuncCalled) + assert.Equal(t, tt.wantPopulateCacheCalled, tt.catalogCachePopulator.populateCacheCalled) }) } } -type mockCatalogCacheRemover struct { - called bool - removeFunc func(catalogName string) error +type mockCatalogCache struct { + removeFuncCalled bool + removeFunc func(catalogName string) error + getFuncCalled bool + getFunc func(catalogName, resolvedRef string) (fs.FS, error) +} + +func (m *mockCatalogCache) Remove(catalogName string) error { + m.removeFuncCalled = true + if m.removeFunc != nil { + return m.removeFunc(catalogName) + } + + return nil +} + +func (m *mockCatalogCache) Get(catalogName, resolvedRef string) (fs.FS, error) { + m.getFuncCalled = true + if m.getFunc != nil { + return m.getFunc(catalogName, resolvedRef) + } + + return nil, nil } -func (m *mockCatalogCacheRemover) Remove(catalogName string) error { - m.called = true - return m.removeFunc(catalogName) +type mockCatalogCachePopulator struct { + populateCacheCalled bool + populateCacheFunc func(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) +} + +func (m *mockCatalogCachePopulator) PopulateCache(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) { + m.populateCacheCalled = true + if m.populateCacheFunc != nil { + return m.populateCacheFunc(ctx, catalog) + } + + return nil, nil } From 482a66d88a105c1407bd39c2d8a1d274be618b9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 10:49:39 -0400 Subject: [PATCH 096/694] :seedling: Bump github.com/onsi/ginkgo/v2 from 2.20.2 to 2.21.0 (#1416) Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.20.2 to 2.21.0. - [Release notes](https://github.com/onsi/ginkgo/releases) - [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/ginkgo/compare/v2.20.2...v2.21.0) --- updated-dependencies: - dependency-name: github.com/onsi/ginkgo/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 36b15ebee3..eeda02948e 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/go-logr/logr v1.4.2 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.2 - github.com/onsi/ginkgo/v2 v2.20.2 + github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.34.2 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 @@ -116,7 +116,7 @@ require ( github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect @@ -230,7 +230,7 @@ require ( golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.25.0 // indirect + golang.org/x/tools v0.26.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect diff --git a/go.sum b/go.sum index bec4549d6d..25ce6000e5 100644 --- a/go.sum +++ b/go.sum @@ -310,8 +310,8 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -518,8 +518,8 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= -github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -892,8 +892,8 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From e30a2d807c486a3a405d2ca0a93be9cf53176840 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Nov 2024 13:32:58 +0000 Subject: [PATCH 097/694] :seedling: Bump watchdog from 4.0.2 to 6.0.0 (#1423) Bumps [watchdog](https://github.com/gorakhargosh/watchdog) from 4.0.2 to 6.0.0. - [Release notes](https://github.com/gorakhargosh/watchdog/releases) - [Changelog](https://github.com/gorakhargosh/watchdog/blob/master/changelog.rst) - [Commits](https://github.com/gorakhargosh/watchdog/compare/v4.0.2...v6.0.0) --- updated-dependencies: - dependency-name: watchdog dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 91d817961e..906260fd95 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,4 +32,4 @@ requests==2.32.3 six==1.16.0 soupsieve==2.6 urllib3==2.2.3 -watchdog==4.0.2 +watchdog==6.0.0 From cfd4bec28827a94e717f824a111ddcac3a144709 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:53:06 +0100 Subject: [PATCH 098/694] :seedling: Bump github.com/onsi/gomega from 1.34.2 to 1.35.1 (#1422) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.34.2 to 1.35.1. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.34.2...v1.35.1) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index eeda02948e..2e92a62765 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.2 github.com/onsi/ginkgo/v2 v2.21.0 - github.com/onsi/gomega v1.34.2 + github.com/onsi/gomega v1.35.1 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 github.com/operator-framework/catalogd v0.35.0 diff --git a/go.sum b/go.sum index 25ce6000e5..c801d4b038 100644 --- a/go.sum +++ b/go.sum @@ -523,8 +523,8 @@ github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= From 6bda277736597142d5863e9884f872b2ffd77b63 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Tue, 5 Nov 2024 16:49:30 -0500 Subject: [PATCH 099/694] Add support for kubectl printer columns (#1421) Unless we add another `.status` field, this is what we can do. Only simple queries starting with `.` can be used. Signed-off-by: Todd Short --- api/v1alpha1/clusterextension_types.go | 5 +++++ ...operatorframework.io_clusterextensions.yaml | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index ad99e72511..78608ba3a6 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -510,6 +510,11 @@ type ClusterExtensionInstallStatus struct { //+kubebuilder:object:root=true //+kubebuilder:resource:scope=Cluster //+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="Installed Bundle",type=string,JSONPath=`.status.install.bundle.name` +//+kubebuilder:printcolumn:name=Version,type=string,JSONPath=`.status.install.bundle.version` +//+kubebuilder:printcolumn:name="Installed",type=string,JSONPath=`.status.conditions[?(@.type=='Installed')].status` +//+kubebuilder:printcolumn:name="Progressing",type=string,JSONPath=`.status.conditions[?(@.type=='Progressing')].status` +//+kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp` // ClusterExtension is the Schema for the clusterextensions API type ClusterExtension struct { diff --git a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml index 61b81606bf..2550101474 100644 --- a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml +++ b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -14,7 +14,23 @@ spec: singular: clusterextension scope: Cluster versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .status.install.bundle.name + name: Installed Bundle + type: string + - jsonPath: .status.install.bundle.version + name: Version + type: string + - jsonPath: .status.conditions[?(@.type=='Installed')].status + name: Installed + type: string + - jsonPath: .status.conditions[?(@.type=='Progressing')].status + name: Progressing + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: description: ClusterExtension is the Schema for the clusterextensions API From 66ecaac0adc02a33fc2a8ec07f37a1a5fb4c4c94 Mon Sep 17 00:00:00 2001 From: Daniel Franz Date: Wed, 6 Nov 2024 11:11:58 -0800 Subject: [PATCH 100/694] Test Registry (#1425) Adds a bare-bones test registry and a folder tree for our test images. Signed-off-by: dtfranz --- Makefile | 26 ++- hack/test/build-push-e2e-catalog.sh | 66 ------- test/e2e/cluster_extension_install_test.go | 2 +- testdata/.gitignore | 2 + testdata/Dockerfile | 12 ++ .../build-test-registry.sh | 51 ++++- .../registry-v1/build-push-e2e-bundle.sh | 84 --------- .../prometheus-operator.v1.0.0/Dockerfile | 15 -- testdata/catalogs/test-catalog-v1.Dockerfile | 6 - testdata/catalogs/test-catalog-v2.Dockerfile | 6 - ...toring.coreos.com_alertmanagerconfigs.yaml | 0 .../monitoring.coreos.com_alertmanagers.yaml | 0 .../monitoring.coreos.com_podmonitors.yaml | 0 .../monitoring.coreos.com_probes.yaml | 0 ...onitoring.coreos.com_prometheusagents.yaml | 0 .../monitoring.coreos.com_prometheuses.yaml | 0 ...monitoring.coreos.com_prometheusrules.yaml | 0 .../monitoring.coreos.com_scrapeconfigs.yaml | 0 ...monitoring.coreos.com_servicemonitors.yaml | 0 .../monitoring.coreos.com_thanosrulers.yaml | 0 .../prometheus-operator_v1_service.yaml | 0 ...metheusoperator.clusterserviceversion.yaml | 0 .../v1.0.0}/metadata/annotations.yaml | 0 .../test-catalog/v1/configs}/.indexignore | 0 .../test-catalog/v1/configs}/catalog.yaml | 6 +- .../test-catalog/v2/configs}/.indexignore | 0 .../test-catalog/v2/configs}/catalog.yaml | 2 +- testdata/push/README.md | 46 +++++ testdata/push/go.mod | 26 +++ testdata/push/go.sum | 60 ++++++ testdata/push/push.go | 178 ++++++++++++++++++ testdata/registry/README.md | 15 ++ testdata/registry/go.mod | 8 + testdata/registry/go.sum | 36 ++++ testdata/registry/registry.go | 40 ++++ 35 files changed, 484 insertions(+), 203 deletions(-) delete mode 100755 hack/test/build-push-e2e-catalog.sh create mode 100644 testdata/.gitignore create mode 100644 testdata/Dockerfile rename hack/test/image-registry.sh => testdata/build-test-registry.sh (61%) delete mode 100755 testdata/bundles/registry-v1/build-push-e2e-bundle.sh delete mode 100644 testdata/bundles/registry-v1/prometheus-operator.v1.0.0/Dockerfile delete mode 100644 testdata/catalogs/test-catalog-v1.Dockerfile delete mode 100644 testdata/catalogs/test-catalog-v2.Dockerfile rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_alertmanagerconfigs.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_alertmanagers.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_podmonitors.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_probes.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_prometheusagents.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_prometheuses.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_prometheusrules.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_scrapeconfigs.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_servicemonitors.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/monitoring.coreos.com_thanosrulers.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/prometheus-operator_v1_service.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/manifests/prometheusoperator.clusterserviceversion.yaml (100%) rename testdata/{bundles/registry-v1/prometheus-operator.v1.0.0 => images/bundles/prometheus-operator/v1.0.0}/metadata/annotations.yaml (100%) rename testdata/{catalogs/test-catalog-v1 => images/catalogs/test-catalog/v1/configs}/.indexignore (100%) rename testdata/{catalogs/test-catalog-v1 => images/catalogs/test-catalog/v1/configs}/catalog.yaml (90%) rename testdata/{catalogs/test-catalog-v2 => images/catalogs/test-catalog/v2/configs}/.indexignore (100%) rename testdata/{catalogs/test-catalog-v2 => images/catalogs/test-catalog/v2/configs}/catalog.yaml (89%) create mode 100644 testdata/push/README.md create mode 100644 testdata/push/go.mod create mode 100644 testdata/push/go.sum create mode 100644 testdata/push/push.go create mode 100644 testdata/registry/README.md create mode 100644 testdata/registry/go.mod create mode 100644 testdata/registry/go.sum create mode 100644 testdata/registry/registry.go diff --git a/Makefile b/Makefile index 49a707b3c3..5e8033f517 100644 --- a/Makefile +++ b/Makefile @@ -165,12 +165,16 @@ test-unit: $(SETUP_ENVTEST) #HELP Run the unit tests $(UNIT_TEST_DIRS) \ -test.gocoverdir=$(ROOT_DIR)/coverage/unit -image-registry: ## Setup in-cluster image registry - ./hack/test/image-registry.sh $(E2E_REGISTRY_NAMESPACE) $(E2E_REGISTRY_NAME) - -build-push-e2e-catalog: ## Build the testdata catalog used for e2e tests and push it to the image registry - ./hack/test/build-push-e2e-catalog.sh $(E2E_REGISTRY_NAMESPACE) $(LOCAL_REGISTRY_HOST)/$(E2E_TEST_CATALOG_V1) - ./hack/test/build-push-e2e-catalog.sh $(E2E_REGISTRY_NAMESPACE) $(LOCAL_REGISTRY_HOST)/$(E2E_TEST_CATALOG_V2) +.PHONY: image-registry +E2E_REGISTRY_IMAGE=localhost/e2e-test-registry:devel +image-registry: export GOOS=linux +image-registry: export GOARCH=amd64 +image-registry: ## Build the testdata catalog used for e2e tests and push it to the image registry + go build $(GO_BUILD_FLAGS) -tags '$(GO_BUILD_TAGS)' -ldflags '$(GO_BUILD_LDFLAGS)' -gcflags '$(GO_BUILD_GCFLAGS)' -asmflags '$(GO_BUILD_ASMFLAGS)' -o ./testdata/registry/bin/registry ./testdata/registry/registry.go + go build $(GO_BUILD_FLAGS) -tags '$(GO_BUILD_TAGS)' -ldflags '$(GO_BUILD_LDFLAGS)' -gcflags '$(GO_BUILD_GCFLAGS)' -asmflags '$(GO_BUILD_ASMFLAGS)' -o ./testdata/push/bin/push ./testdata/push/push.go + $(CONTAINER_RUNTIME) build -f ./testdata/Dockerfile -t $(E2E_REGISTRY_IMAGE) ./testdata + $(CONTAINER_RUNTIME) save $(E2E_REGISTRY_IMAGE) | $(KIND) load image-archive /dev/stdin --name $(KIND_CLUSTER_NAME) + ./testdata/build-test-registry.sh $(E2E_REGISTRY_NAMESPACE) $(E2E_REGISTRY_NAME) $(E2E_REGISTRY_IMAGE) # When running the e2e suite, you can set the ARTIFACT_PATH variable to the absolute path # of the directory for the operator-controller e2e tests to store the artifacts, which @@ -181,7 +185,7 @@ build-push-e2e-catalog: ## Build the testdata catalog used for e2e tests and pus test-e2e: KIND_CLUSTER_NAME := operator-controller-e2e test-e2e: KUSTOMIZE_BUILD_DIR := config/overlays/e2e test-e2e: GO_BUILD_FLAGS := -cover -test-e2e: run image-registry build-push-e2e-catalog registry-load-bundles e2e e2e-coverage kind-clean #HELP Run e2e test suite on local kind cluster +test-e2e: run image-registry e2e e2e-coverage kind-clean #HELP Run e2e test suite on local kind cluster .PHONY: extension-developer-e2e extension-developer-e2e: KUSTOMIZE_BUILD_DIR := config/overlays/cert-manager @@ -205,7 +209,7 @@ post-upgrade-checks: test-upgrade-e2e: KIND_CLUSTER_NAME := operator-controller-upgrade-e2e test-upgrade-e2e: export TEST_CLUSTER_CATALOG_NAME := test-catalog test-upgrade-e2e: export TEST_CLUSTER_EXTENSION_NAME := test-package -test-upgrade-e2e: kind-cluster run-latest-release image-registry build-push-e2e-catalog registry-load-bundles pre-upgrade-setup docker-build kind-load kind-deploy post-upgrade-checks kind-clean #HELP Run upgrade e2e tests on a local kind cluster +test-upgrade-e2e: kind-cluster run-latest-release image-registry pre-upgrade-setup docker-build kind-load kind-deploy post-upgrade-checks kind-clean #HELP Run upgrade e2e tests on a local kind cluster .PHONY: e2e-coverage e2e-coverage: @@ -231,12 +235,6 @@ kind-cluster: $(KIND) #EXHELP Standup a kind cluster. kind-clean: $(KIND) #EXHELP Delete the kind cluster. $(KIND) delete cluster --name $(KIND_CLUSTER_NAME) -registry-load-bundles: ## Load selected e2e testdata container images created in kind-load-bundles into registry - testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(LOCAL_REGISTRY_HOST)/bundles/registry-v1/prometheus-operator:v1.0.0 prometheus-operator.v1.0.0 prometheus-operator.v1.0.0 - testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(LOCAL_REGISTRY_HOST)/bundles/registry-v1/prometheus-operator:v1.0.1 prometheus-operator.v1.0.1 prometheus-operator.v1.0.0 - testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(LOCAL_REGISTRY_HOST)/bundles/registry-v1/prometheus-operator:v1.2.0 prometheus-operator.v1.2.0 prometheus-operator.v1.0.0 - testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(LOCAL_REGISTRY_HOST)/bundles/registry-v1/prometheus-operator:v2.0.0 prometheus-operator.v2.0.0 prometheus-operator.v1.0.0 - #SECTION Build ifeq ($(origin VERSION), undefined) diff --git a/hack/test/build-push-e2e-catalog.sh b/hack/test/build-push-e2e-catalog.sh deleted file mode 100755 index 9fd1a9d6b2..0000000000 --- a/hack/test/build-push-e2e-catalog.sh +++ /dev/null @@ -1,66 +0,0 @@ -#! /bin/bash - -set -o errexit -set -o nounset -set -o pipefail - -help=" -build-push-e2e-catalog.sh is a script to build and push the e2e catalog image using kaniko. -Usage: - build-push-e2e-catalog.sh [NAMESPACE] [TAG] - -Argument Descriptions: - - NAMESPACE is the namespace the kaniko Job should be created in - - TAG is the full tag used to build and push the catalog image -" - -if [[ "$#" -ne 2 ]]; then - echo "Illegal number of arguments passed" - echo "${help}" - exit 1 -fi - -namespace=$1 -image=$2 -tag=${image##*:} - -echo "${namespace}" "${image}" "${tag}" - -kubectl create configmap -n "${namespace}" --from-file=testdata/catalogs/test-catalog-${tag}.Dockerfile operator-controller-e2e-${tag}.dockerfile -kubectl create configmap -n "${namespace}" --from-file=testdata/catalogs/test-catalog-${tag} operator-controller-e2e-${tag}.build-contents - -kubectl apply -f - << EOF -apiVersion: batch/v1 -kind: Job -metadata: - name: "kaniko-${tag}" - namespace: "${namespace}" -spec: - template: - spec: - containers: - - name: kaniko-${tag} - image: gcr.io/kaniko-project/executor:latest - args: ["--dockerfile=/workspace/test-catalog-${tag}.Dockerfile", - "--context=/workspace/", - "--destination=${image}", - "--skip-tls-verify"] - volumeMounts: - - name: dockerfile - mountPath: /workspace/ - - name: build-contents - mountPath: /workspace/test-catalog-${tag}/ - restartPolicy: Never - volumes: - - name: dockerfile - configMap: - name: operator-controller-e2e-${tag}.dockerfile - items: - - key: test-catalog-${tag}.Dockerfile - path: test-catalog-${tag}.Dockerfile - - name: build-contents - configMap: - name: operator-controller-e2e-${tag}.build-contents -EOF - -kubectl wait --for=condition=Complete -n "${namespace}" jobs/kaniko-${tag} --timeout=60s diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index d03aacf5e5..6efe98ca83 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -550,7 +550,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { // patch imageRef tag on test-catalog image with v2 image t.Log("By patching the catalog ImageRef to point to the v2 catalog") - updatedCatalogImage := fmt.Sprintf("%s/e2e/test-catalog:v2", os.Getenv("LOCAL_REGISTRY_HOST")) + updatedCatalogImage := fmt.Sprintf("%s/test-catalog:v2", os.Getenv("LOCAL_REGISTRY_HOST")) err := patchTestCatalog(context.Background(), testCatalogName, updatedCatalogImage) require.NoError(t, err) require.EventuallyWithT(t, func(ct *assert.CollectT) { diff --git a/testdata/.gitignore b/testdata/.gitignore new file mode 100644 index 0000000000..1eca1dc7e4 --- /dev/null +++ b/testdata/.gitignore @@ -0,0 +1,2 @@ +push/bin +registry/bin diff --git a/testdata/Dockerfile b/testdata/Dockerfile new file mode 100644 index 0000000000..0f1355f56d --- /dev/null +++ b/testdata/Dockerfile @@ -0,0 +1,12 @@ +from gcr.io/distroless/static:nonroot + +WORKDIR / + +COPY registry/bin/registry registry +COPY push/bin/push push + +COPY images images + +EXPOSE 5000 + +USER 65532:65532 diff --git a/hack/test/image-registry.sh b/testdata/build-test-registry.sh similarity index 61% rename from hack/test/image-registry.sh rename to testdata/build-test-registry.sh index ac3e68eb7c..3ea0e65b0e 100755 --- a/hack/test/image-registry.sh +++ b/testdata/build-test-registry.sh @@ -1,22 +1,21 @@ -#! /bin/bash +#!/bin/bash set -o errexit set -o nounset set -o pipefail help=" -image-registry.sh is a script to stand up an image registry within a cluster. +build-test-registry.sh is a script to stand up an image registry within a cluster. Usage: - image-registry.sh [NAMESPACE] [NAME] [CERT_REF] + build-test-registry.sh [NAMESPACE] [NAME] [IMAGE] Argument Descriptions: - NAMESPACE is the namespace that should be created and is the namespace in which the image registry will be created - NAME is the name that should be used for the image registry Deployment and Service - - CERT_REF is the reference to the CA certificate that should be used to serve the image registry over HTTPS, in the - format of 'Issuer/' or 'ClusterIssuer/' + - IMAGE is the name of the image that should be used to run the image registry " -if [[ "$#" -ne 2 ]]; then +if [[ "$#" -ne 3 ]]; then echo "Illegal number of arguments passed" echo "${help}" exit 1 @@ -24,6 +23,7 @@ fi namespace=$1 name=$2 +image=$3 kubectl apply -f - << EOF apiVersion: v1 @@ -69,7 +69,12 @@ spec: spec: containers: - name: registry - image: registry:2 + image: ${image} + imagePullPolicy: IfNotPresent + command: + - /registry + args: + - "--registry-address=:5000" volumeMounts: - name: certs-vol mountPath: "/certs" @@ -100,3 +105,35 @@ spec: EOF kubectl wait --for=condition=Available -n "${namespace}" "deploy/${name}" --timeout=60s + +kubectl apply -f - << EOF +apiVersion: batch/v1 +kind: Job +metadata: + name: ${name}-push + namespace: "${namespace}" +spec: + template: + spec: + restartPolicy: Never + containers: + - name: push + image: ${image} + command: + - /push + args: + - "--registry-address=${name}.${namespace}.svc:5000" + - "--images-path=/images" + volumeMounts: + - name: certs-vol + mountPath: "/certs" + env: + - name: SSL_CERT_DIR + value: "/certs/" + volumes: + - name: certs-vol + secret: + secretName: ${namespace}-registry +EOF + +kubectl wait --for=condition=Complete -n "${namespace}" "job/${name}-push" --timeout=60s diff --git a/testdata/bundles/registry-v1/build-push-e2e-bundle.sh b/testdata/bundles/registry-v1/build-push-e2e-bundle.sh deleted file mode 100755 index 0aec13cc9f..0000000000 --- a/testdata/bundles/registry-v1/build-push-e2e-bundle.sh +++ /dev/null @@ -1,84 +0,0 @@ -#! /bin/bash - -set -o errexit -set -o nounset -set -o pipefail - -help=" -build-push-e2e-bundle.sh is a script to build and push the e2e bundle image using kaniko. -Usage: - build-push-e2e-bundle.sh [NAMESPACE] [TAG] [BUNDLE_NAME] [BUNDLE_DIR] - -Argument Descriptions: - - NAMESPACE is the namespace the kaniko Job should be created in - - TAG is the full tag used to build and push the catalog image -" - -if [[ "$#" -ne 4 ]]; then - echo "Illegal number of arguments passed" - echo "${help}" - exit 1 -fi - - -namespace=$1 -tag=$2 -bundle_name=$3 -package_name=$4 -bundle_dir="testdata/bundles/registry-v1/${package_name}" - -echo "${namespace}" "${tag}" - -kubectl create configmap -n "${namespace}" --from-file="${bundle_dir}/Dockerfile" operator-controller-e2e-${bundle_name}.root - -tgz="${bundle_dir}/manifests.tgz" -tar czf "${tgz}" -C "${bundle_dir}/" manifests metadata -kubectl create configmap -n "${namespace}" --from-file="${tgz}" operator-controller-${bundle_name}.manifests -rm "${tgz}" - -# Remove periods from bundle name due to pod name issues -job_name=${bundle_name//.} - -kubectl apply -f - << EOF -apiVersion: batch/v1 -kind: Job -metadata: - name: "kaniko-${job_name}" - namespace: "${namespace}" -spec: - template: - spec: - initContainers: - - name: copy-manifests - image: busybox - command: ['sh', '-c', 'cp /manifests-data/* /manifests'] - volumeMounts: - - name: manifests - mountPath: /manifests - - name: manifests-data - mountPath: /manifests-data - containers: - - name: kaniko - image: gcr.io/kaniko-project/executor:latest - args: ["--dockerfile=/workspace/Dockerfile", - "--context=tar:///workspace/manifests/manifests.tgz", - "--destination=${tag}", - "--skip-tls-verify"] - volumeMounts: - - name: dockerfile - mountPath: /workspace/ - - name: manifests - mountPath: /workspace/manifests/ - restartPolicy: Never - volumes: - - name: dockerfile - configMap: - name: operator-controller-e2e-${bundle_name}.root - - name: manifests - emptyDir: {} - - name: manifests-data - configMap: - name: operator-controller-${bundle_name}.manifests -EOF - -kubectl wait --for=condition=Complete -n "${namespace}" jobs/kaniko-${job_name} --timeout=60s diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/Dockerfile b/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/Dockerfile deleted file mode 100644 index 5a14581489..0000000000 --- a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -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=prometheusoperator -LABEL operators.operatorframework.io.bundle.channels.v1=beta -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.28.0 -LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 -LABEL operators.operatorframework.io.metrics.project_layout=unknown - -# Copy files to locations specified by labels. -COPY manifests /manifests/ -COPY metadata /metadata/ diff --git a/testdata/catalogs/test-catalog-v1.Dockerfile b/testdata/catalogs/test-catalog-v1.Dockerfile deleted file mode 100644 index d255e07748..0000000000 --- a/testdata/catalogs/test-catalog-v1.Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM scratch -ADD test-catalog-v1 /configs - -# Set DC-specific label for the location of the DC root directory -# in the image -LABEL operators.operatorframework.io.index.configs.v1=/configs diff --git a/testdata/catalogs/test-catalog-v2.Dockerfile b/testdata/catalogs/test-catalog-v2.Dockerfile deleted file mode 100644 index 72b3a7a2e6..0000000000 --- a/testdata/catalogs/test-catalog-v2.Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM scratch -ADD test-catalog-v2 /configs - -# Set DC-specific label for the location of the DC root directory -# in the image -LABEL operators.operatorframework.io.index.configs.v1=/configs diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_alertmanagerconfigs.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagerconfigs.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_alertmanagerconfigs.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagerconfigs.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_alertmanagers.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagers.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_alertmanagers.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagers.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_podmonitors.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_podmonitors.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_podmonitors.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_podmonitors.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_probes.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_probes.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_probes.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_probes.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_prometheusagents.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheusagents.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_prometheusagents.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheusagents.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_prometheuses.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheuses.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_prometheuses.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheuses.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_prometheusrules.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheusrules.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_prometheusrules.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheusrules.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_scrapeconfigs.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_scrapeconfigs.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_scrapeconfigs.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_scrapeconfigs.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_servicemonitors.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_servicemonitors.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_servicemonitors.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_servicemonitors.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_thanosrulers.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_thanosrulers.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/monitoring.coreos.com_thanosrulers.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_thanosrulers.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/prometheus-operator_v1_service.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/prometheus-operator_v1_service.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/prometheus-operator_v1_service.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/prometheus-operator_v1_service.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/prometheusoperator.clusterserviceversion.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/prometheusoperator.clusterserviceversion.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/manifests/prometheusoperator.clusterserviceversion.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/manifests/prometheusoperator.clusterserviceversion.yaml diff --git a/testdata/bundles/registry-v1/prometheus-operator.v1.0.0/metadata/annotations.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/metadata/annotations.yaml similarity index 100% rename from testdata/bundles/registry-v1/prometheus-operator.v1.0.0/metadata/annotations.yaml rename to testdata/images/bundles/prometheus-operator/v1.0.0/metadata/annotations.yaml diff --git a/testdata/catalogs/test-catalog-v1/.indexignore b/testdata/images/catalogs/test-catalog/v1/configs/.indexignore similarity index 100% rename from testdata/catalogs/test-catalog-v1/.indexignore rename to testdata/images/catalogs/test-catalog/v1/configs/.indexignore diff --git a/testdata/catalogs/test-catalog-v1/catalog.yaml b/testdata/images/catalogs/test-catalog/v1/configs/catalog.yaml similarity index 90% rename from testdata/catalogs/test-catalog-v1/catalog.yaml rename to testdata/images/catalogs/test-catalog/v1/configs/catalog.yaml index 41c843d72c..ac1613ad15 100644 --- a/testdata/catalogs/test-catalog-v1/catalog.yaml +++ b/testdata/images/catalogs/test-catalog/v1/configs/catalog.yaml @@ -32,7 +32,7 @@ properties: schema: olm.bundle name: prometheus-operator.1.0.1 package: prometheus -image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v1.0.1 +image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v1.0.0 properties: - type: olm.package value: @@ -42,7 +42,7 @@ properties: schema: olm.bundle name: prometheus-operator.1.2.0 package: prometheus -image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v1.2.0 +image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v1.0.0 properties: - type: olm.package value: @@ -63,7 +63,7 @@ entries: schema: olm.bundle name: prometheus-mirrored-operator.1.2.0 package: prometheus-mirrored -image: mirrored-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v1.2.0 +image: mirrored-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v1.0.0 properties: - type: olm.package value: diff --git a/testdata/catalogs/test-catalog-v2/.indexignore b/testdata/images/catalogs/test-catalog/v2/configs/.indexignore similarity index 100% rename from testdata/catalogs/test-catalog-v2/.indexignore rename to testdata/images/catalogs/test-catalog/v2/configs/.indexignore diff --git a/testdata/catalogs/test-catalog-v2/catalog.yaml b/testdata/images/catalogs/test-catalog/v2/configs/catalog.yaml similarity index 89% rename from testdata/catalogs/test-catalog-v2/catalog.yaml rename to testdata/images/catalogs/test-catalog/v2/configs/catalog.yaml index 7208809cc0..779d5cc4f9 100644 --- a/testdata/catalogs/test-catalog-v2/catalog.yaml +++ b/testdata/images/catalogs/test-catalog/v2/configs/catalog.yaml @@ -13,7 +13,7 @@ entries: schema: olm.bundle name: prometheus-operator.2.0.0 package: prometheus -image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v2.0.0 +image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v1.0.0 properties: - type: olm.package value: diff --git a/testdata/push/README.md b/testdata/push/README.md new file mode 100644 index 0000000000..f45f0cc460 --- /dev/null +++ b/testdata/push/README.md @@ -0,0 +1,46 @@ +# Test Registry Image Push + +This tool builds our test bundle and catalog images via crane. It accepts two command line arguments: +``` +Usage of push: + --images-path string Image directory path (default "/images") + --registry-address string The address of the registry. (default ":12345") +``` + +`--registry-address` is the address of the registry to be pushed to. + +`--images-path` should point to the root directory of the images tree structure. The tool expects a particular directory format in order to work properly. Bundles should be placed in `/bundles`, and catalogs in `/catalogs`. From these directories the same convention should be followed: folders within `[catalogs|bundles]` are image names i.e. `test-catalog`. Within these folders is where each tag for that image should be placed. What that ends up looking like is: +```bash +$ tree ./testdata/images/ +./testdata/images/ +├── bundles +│   └── prometheus-operator +│   └── v1.0.0 +│ ├── metadata +│       │ └── annotations.yaml +│      └── manifests +│       └── example.yaml +└── catalogs + └── test-catalog +    ├── v1 +    │   └── configs +    │   └── catalog.yaml +    └── v2 +    └── configs +    └── catalog.yaml +``` +The inside of each tag folder will be placed directly into `/` of the built container i.e. `test-catalog:v1` will have `/configs/catalog.yaml`. + +To add a new image or tag for the tool, create the folders required and populate them with the files to be mounted. Bundle images requiring metadata should contain a `metadata` folder with `annotations.yaml`. Example content: +```yaml +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: prometheus + operators.operatorframework.io.bundle.channels.v1: beta + operators.operatorframework.io.metrics.builder: operator-sdk-v1.28.0 + operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 + operators.operatorframework.io.metrics.project_layout: unknown +``` diff --git a/testdata/push/go.mod b/testdata/push/go.mod new file mode 100644 index 0000000000..72bb1b9e86 --- /dev/null +++ b/testdata/push/go.mod @@ -0,0 +1,26 @@ +module registry + +go 1.22.5 + +require ( + github.com/google/go-containerregistry v0.20.2 + github.com/spf13/pflag v1.0.5 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect + github.com/docker/cli v27.3.1+incompatible // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker-credential-helpers v0.8.2 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/vbatts/tar-split v0.11.6 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect +) diff --git a/testdata/push/go.sum b/testdata/push/go.sum new file mode 100644 index 0000000000..6f7cce0f87 --- /dev/null +++ b/testdata/push/go.sum @@ -0,0 +1,60 @@ +github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU= +github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ= +github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= +github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= +github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= +github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= diff --git a/testdata/push/push.go b/testdata/push/push.go new file mode 100644 index 0000000000..72989b1dc9 --- /dev/null +++ b/testdata/push/push.go @@ -0,0 +1,178 @@ +package main + +import ( + "flag" + "fmt" + "io/fs" + "log" + "os" + "strings" + + "github.com/google/go-containerregistry/pkg/crane" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/mutate" + "github.com/spf13/pflag" + "gopkg.in/yaml.v2" +) + +const ( + bundlesSubPath string = "bundles" + catalogsSubPath string = "catalogs" +) + +func main() { + var ( + registryAddr string + imagesPath string + ) + flag.StringVar(®istryAddr, "registry-address", ":12345", "The address the registry binds to.") + flag.StringVar(&imagesPath, "images-path", "/images", "Image directory path") + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + + log.Printf("push operation configured with images path %s and destination %s", imagesPath, registryAddr) + + bundlesFullPath := fmt.Sprintf("%s/%s", imagesPath, bundlesSubPath) + catalogsFullPath := fmt.Sprintf("%s/%s", imagesPath, catalogsSubPath) + + bundles, err := buildBundles(bundlesFullPath) + if err != nil { + log.Fatalf("failed to build bundles: %s", err.Error()) + } + catalogs, err := buildCatalogs(catalogsFullPath) + if err != nil { + log.Fatalf("failed to build catalogs: %s", err.Error()) + } + // Push the images + for name, image := range bundles { + if err := crane.Push(image, fmt.Sprintf("%s/%s", registryAddr, name)); err != nil { + log.Fatalf("failed to push bundle images: %s", err.Error()) + } + } + for name, image := range catalogs { + if err := crane.Push(image, fmt.Sprintf("%s/%s", registryAddr, name)); err != nil { + log.Fatalf("failed to push catalog images: %s", err.Error()) + } + } + log.Printf("finished") + os.Exit(0) +} + +func buildBundles(path string) (map[string]v1.Image, error) { + bundles, err := processImageDirTree(path) + if err != nil { + return nil, err + } + mutatedMap := make(map[string]v1.Image, 0) + // Apply required bundle labels + for key, img := range bundles { + // Replace ':' between image name and image tag for file path + metadataPath := strings.Replace(key, ":", "/", 1) + labels, err := getBundleLabels(fmt.Sprintf("%s/%s/%s", path, metadataPath, "metadata/annotations.yaml")) + if err != nil { + return nil, err + } + mutatedMap[fmt.Sprintf("bundles/registry-v1/%s", key)], err = mutate.Config(img, v1.Config{Labels: labels}) + if err != nil { + return nil, fmt.Errorf("failed to apply image labels: %w", err) + } + } + return mutatedMap, nil +} + +type bundleAnnotations struct { + annotations map[string]string +} + +func getBundleLabels(path string) (map[string]string, error) { + var metadata bundleAnnotations + yamlFile, err := os.ReadFile(path) + if err != nil { + return nil, err + } + err = yaml.Unmarshal(yamlFile, metadata) + if err != nil { + return nil, err + } + return metadata.annotations, nil +} + +func buildCatalogs(path string) (map[string]v1.Image, error) { + catalogs, err := processImageDirTree(path) + if err != nil { + return nil, err + } + mutatedMap := make(map[string]v1.Image, 0) + // Apply required catalog label + for key, img := range catalogs { + cfg := v1.Config{ + Labels: map[string]string{ + "operators.operatorframework.io.index.configs.v1": "/configs", + }, + } + mutatedMap[fmt.Sprintf("e2e/%s", key)], err = mutate.Config(img, cfg) + if err != nil { + return nil, fmt.Errorf("failed to apply image labels: %w", err) + } + } + return mutatedMap, nil +} + +func processImageDirTree(path string) (map[string]v1.Image, error) { + imageMap := make(map[string]v1.Image, 0) + images, err := os.ReadDir(path) + if err != nil { + return nil, err + } + + // Each directory in 'path' represents an image + for _, entry := range images { + entryFullPath := fmt.Sprintf("%s/%s", path, entry.Name()) + if !entry.IsDir() { + continue + } + tags, err := os.ReadDir(entryFullPath) + if err != nil { + return nil, err + } + // Each directory in the image directory represents a separate tag + for _, tag := range tags { + if !tag.IsDir() { + continue + } + tagFullPath := fmt.Sprintf("%s/%s", entryFullPath, tag.Name()) + + var fileMap map[string][]byte + fileMap, err = createFileMap(tagFullPath) + if err != nil { + return nil, fmt.Errorf("failed to read files for image: %w", err) + } + + image, err := crane.Image(fileMap) + if err != nil { + return nil, fmt.Errorf("failed to generate image: %w", err) + } + imageMap[fmt.Sprintf("%s:%s", entry.Name(), tag.Name())] = image + } + } + return imageMap, nil +} + +func createFileMap(originPath string) (map[string][]byte, error) { + fileMap := make(map[string][]byte) + if err := fs.WalkDir(os.DirFS(originPath), ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d != nil && !d.IsDir() { + fileMap[path], err = os.ReadFile(fmt.Sprintf("%s/%s", originPath, path)) + if err != nil { + return err + } + } + return nil + }); err != nil { + return nil, err + } + return fileMap, nil +} diff --git a/testdata/registry/README.md b/testdata/registry/README.md new file mode 100644 index 0000000000..18c41722a7 --- /dev/null +++ b/testdata/registry/README.md @@ -0,0 +1,15 @@ +# Test Registry + +This tool is a bare-bones image registry using the `go-containerregistry` library; it is intended to be used in a test environment only. + +Usage: +``` +Usage of registry: + --registry-address string The address the registry binds to. (default ":12345") +``` + +The server key and cert locations should be set under the following environment variables: +``` + REGISTRY_HTTP_TLS_CERTIFICATE + REGISTRY_HTTP_TLS_KEY +``` diff --git a/testdata/registry/go.mod b/testdata/registry/go.mod new file mode 100644 index 0000000000..ce79002d4b --- /dev/null +++ b/testdata/registry/go.mod @@ -0,0 +1,8 @@ +module registry + +go 1.22.5 + +require ( + github.com/google/go-containerregistry v0.20.2 + github.com/spf13/pflag v1.0.5 +) diff --git a/testdata/registry/go.sum b/testdata/registry/go.sum new file mode 100644 index 0000000000..ebadf4aec1 --- /dev/null +++ b/testdata/registry/go.sum @@ -0,0 +1,36 @@ +github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= +github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= +github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= +github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= +github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= +github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ= +github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= +github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= diff --git a/testdata/registry/registry.go b/testdata/registry/registry.go new file mode 100644 index 0000000000..553d9bcd43 --- /dev/null +++ b/testdata/registry/registry.go @@ -0,0 +1,40 @@ +package main + +import ( + "flag" + "log" + "net/http" + "os" + "time" + + "github.com/google/go-containerregistry/pkg/registry" + "github.com/spf13/pflag" +) + +const ( + certEnv = "REGISTRY_HTTP_TLS_CERTIFICATE" + keyEnv = "REGISTRY_HTTP_TLS_KEY" +) + +func main() { + var ( + registryAddr string + ) + flag.StringVar(®istryAddr, "registry-address", ":12345", "The address the registry binds to.") + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + + s := &http.Server{ + Addr: registryAddr, + Handler: registry.New(), + ReadTimeout: 60 * time.Second, + WriteTimeout: 60 * time.Second, + } + + err := s.ListenAndServeTLS(os.Getenv(certEnv), os.Getenv(keyEnv)) + if err != nil { + log.Fatalf("failed to start image registry: %s", err.Error()) + } + + defer s.Close() +} From 570d760f13e1ba4dcc0f162879187aab0f88ef27 Mon Sep 17 00:00:00 2001 From: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:12:59 +0000 Subject: [PATCH 101/694] chore: configure Codecov to include only relevant directories for coverage (#1431) - Updated .codecov.yml to include only specific directories (api/, cmd/, internal/) in coverage reports - Excluded documentation, YAML configurations, and test files to improve coverage accuracy and prevent irrelevant CI coverage failures --- codecov.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/codecov.yml b/codecov.yml index 4dde336da3..a3bfabd611 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,3 +1,11 @@ codecov: notify: after_n_builds: 2 + +# Configure the paths to include in coverage reports. +# Exclude documentation, YAML configurations, and test files. +coverage: + paths: + - "api/" + - "cmd/" + - "internal/" \ No newline at end of file From fca0afacc35c9dcf9dae3914e1427e31398b2f92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:32:19 +0100 Subject: [PATCH 102/694] :seedling: Bump mkdocs-material from 9.5.43 to 9.5.44 (#1427) Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.43 to 9.5.44. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.43...9.5.44) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 906260fd95..8d436fb6dd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ markdown2==2.5.1 MarkupSafe==3.0.2 mergedeep==1.3.4 mkdocs==1.6.1 -mkdocs-material==9.5.43 +mkdocs-material==9.5.44 mkdocs-material-extensions==1.3.1 packaging==24.1 paginate==0.5.7 From 22004fa4d8d716d4fa2516e809fc19fa5a61ff91 Mon Sep 17 00:00:00 2001 From: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:23:24 +0000 Subject: [PATCH 103/694] docs: add local build and deploy instructions to CONTRIBUTING.md (#1430) - Added steps for building, loading Docker images, and deploying changes locally with Kind --- CONTRIBUTING.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dbd4508d3e..2ca434ce1b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,6 +36,29 @@ The workflow defined above implies that the community is always ready for discus Please keep this workflow in mind as you read through the document. +## How to Build and Deploy Locally + +After creating a fork and cloning the project locally, +you can follow the steps below to test your changes: + +1. Create the cluster: + +```sh +kind create cluster operator-controller +``` + +2. Build your changes: + +```sh +make build docker-build +``` + +3. Load the image locally and Deploy to Kind + +```sh +make kind-load kind-deploy +``` + ### Communication Channels - Email: [operator-framework-olm-dev](mailto:operator-framework-olm-dev@googlegroups.com) From 481bd5d8686d0fb35a986f87f44691dcc3af22f1 Mon Sep 17 00:00:00 2001 From: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:13:34 +0000 Subject: [PATCH 104/694] fix(docs): update API name to correct version (#1433) Replace `apiVersion: catalogd.operatorframework.io/v1alpha1` with `apiVersion: olm.operatorframework.io/v1alpha1` for `clustercatalogs.olm.operatorframework.io`. Reason: The API version referenced in the docs appears incorrect. After installing, the correct API name is confirmed with: ``` $ kubectl get crds | grep clustercatalog clustercatalogs.olm.operatorframework.io 2024-11-06T21:34:38Z ``` The API is listed under `clustercatalogs.olm.operatorframework.io`, not `catalogd`. Example error faced when trying to apply with the incorrect API name: ``` $ kubectl apply -f - < apiVersion: catalogd.operatorframework.io/v1alpha1 > kind: ClusterCatalog > metadata: > name: operatorhubio > spec: > source: > type: Image > image: > ref: quay.io/operatorhubio/catalog:latest > pollInterval: 10m > EOF error: resource mapping not found for name: "operatorhubio" namespace: "" from "STDIN": no matches for kind "ClusterCatalog" in version "catalogd.operatorframework.io/v1alpha1" ensure CRDs are installed first ``` For reference, see the correct API name here: [olm.operatorframework.io_clustercatalogs.yaml](https://github.com/operator-framework/catalogd/blob/main/config/base/crd/bases/olm.operatorframework.io_clustercatalogs.yaml). --- docs/concepts/controlling-catalog-selection.md | 6 +++--- docs/getting-started/olmv1_getting_started.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/concepts/controlling-catalog-selection.md b/docs/concepts/controlling-catalog-selection.md index 68d19c2b3e..13b782d0fe 100644 --- a/docs/concepts/controlling-catalog-selection.md +++ b/docs/concepts/controlling-catalog-selection.md @@ -125,7 +125,7 @@ When multiple catalogs provide the same package, you can set priorities to resol In your `ClusterCatalog` resource, set the `priority` field: ```yaml -apiVersion: catalogd.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1alpha1 kind: ClusterCatalog metadata: name: high-priority-catalog @@ -160,7 +160,7 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera 1. **Create or Update `ClusterCatalogs` with Appropriate Labels and Priority** ```yaml - apiVersion: catalogd.operatorframework.io/v1alpha1 + apiVersion: olm.operatorframework.io/v1alpha1 kind: ClusterCatalog metadata: name: catalog-a @@ -175,7 +175,7 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera ``` ```yaml - apiVersion: catalogd.operatorframework.io/v1alpha1 + apiVersion: olm.operatorframework.io/v1alpha1 kind: ClusterCatalog metadata: name: catalog-b diff --git a/docs/getting-started/olmv1_getting_started.md b/docs/getting-started/olmv1_getting_started.md index 1156bc968b..efffddde88 100644 --- a/docs/getting-started/olmv1_getting_started.md +++ b/docs/getting-started/olmv1_getting_started.md @@ -35,7 +35,7 @@ To create the catalog, run the following command: ```bash # Create ClusterCatalog kubectl apply -f - < Date: Thu, 7 Nov 2024 15:11:06 +0100 Subject: [PATCH 105/694] Bump catalogd to v0.36.0 (#1434) Signed-off-by: Mikalai Radchuk --- docs/api-reference/catalogd-api-reference.md | 56 +++++++++++++------- go.mod | 8 +-- go.sum | 16 +++--- internal/authentication/tokengetter.go | 2 +- internal/resolve/catalog.go | 2 +- internal/resolve/catalog_test.go | 12 ++--- test/e2e/e2e_suite_test.go | 6 +-- 7 files changed, 61 insertions(+), 41 deletions(-) diff --git a/docs/api-reference/catalogd-api-reference.md b/docs/api-reference/catalogd-api-reference.md index 90ec139048..83c1558355 100644 --- a/docs/api-reference/catalogd-api-reference.md +++ b/docs/api-reference/catalogd-api-reference.md @@ -22,6 +22,23 @@ Package v1alpha1 contains API Schema definitions for the core v1alpha1 API group +#### AvailabilityMode + +_Underlying type:_ _string_ + +AvailabilityMode defines the availability of the catalog + + + +_Appears in:_ +- [ClusterCatalogSpec](#clustercatalogspec) + +| Field | Description | +| --- | --- | +| `Available` | | +| `Unavailable` | | + + #### CatalogSource @@ -36,8 +53,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `type` _[SourceType](#sourcetype)_ | type is a required reference to the type of source the catalog is sourced from.

Allowed values are ["Image"]

When this field is set to "Image", the ClusterCatalog content will be sourced from an OCI image.
When using an image source, the image field must be set and must be the only field defined for this type. | | Enum: [Image]
Required: \{\}
| -| `image` _[ImageSource](#imagesource)_ | image is used to configure how catalog contents are sourced from an OCI image. This field must be set when type is set to "Image" and must be the only field defined for this type. | | | +| `type` _[SourceType](#sourcetype)_ | type is a reference to the type of source the catalog is sourced from.
type is required.

The only allowed value is "Image".

When set to "Image", the ClusterCatalog content will be sourced from an OCI image.
When using an image source, the image field must be set and must be the only field defined for this type. | | Enum: [Image]
Required: \{\}
| +| `image` _[ImageSource](#imagesource)_ | image is used to configure how catalog contents are sourced from an OCI image.
This field is required when type is Image, and forbidden otherwise. | | | #### ClusterCatalog @@ -59,8 +76,8 @@ _Appears in:_ | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `spec` _[ClusterCatalogSpec](#clustercatalogspec)_ | | | | -| `status` _[ClusterCatalogStatus](#clustercatalogstatus)_ | | | | +| `spec` _[ClusterCatalogSpec](#clustercatalogspec)_ | spec is the desired state of the ClusterCatalog.
spec is required.
The controller will work to ensure that the desired
catalog is unpacked and served over the catalog content HTTP server. | | Required: \{\}
| +| `status` _[ClusterCatalogStatus](#clustercatalogstatus)_ | status contains information about the state of the ClusterCatalog such as:
- Whether or not the catalog contents are being served via the catalog content HTTP server
- Whether or not the ClusterCatalog is progressing to a new state
- A reference to the source from which the catalog contents were retrieved | | | #### ClusterCatalogList @@ -80,7 +97,7 @@ ClusterCatalogList contains a list of ClusterCatalog | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | | `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `items` _[ClusterCatalog](#clustercatalog) array_ | | | | +| `items` _[ClusterCatalog](#clustercatalog) array_ | items is a list of ClusterCatalogs.
items is required. | | Required: \{\}
| #### ClusterCatalogSpec @@ -96,9 +113,9 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `source` _[CatalogSource](#catalogsource)_ | source is a required field that allows the user to define the source of a Catalog that contains catalog metadata in the File-Based Catalog (FBC) format.

Below is a minimal example of a ClusterCatalogSpec that sources a catalog from an image:

source:
type: Image
image:
ref: quay.io/operatorhubio/catalog:latest

For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs | | | -| `priority` _integer_ | priority is an optional field that allows the user to define a priority for a ClusterCatalog.
A ClusterCatalog's priority is used by clients as a tie-breaker between ClusterCatalogs that meet the client's requirements.
For example, in the case where multiple ClusterCatalogs provide the same bundle.
A higher number means higher priority. Negative numbers are also accepted.
When omitted, the default priority is 0. | 0 | | -| `availability` _string_ | Availability is an optional field that allows users to define whether the ClusterCatalog is utilized by the operator-controller.

Allowed values are : ["Enabled", "Disabled"].
If set to "Enabled", the catalog will be used for updates, serving contents, and package installations.

If set to "Disabled", catalogd will stop serving the catalog and the cached data will be removed.

If unspecified, the default value is "Enabled" | Enabled | Enum: [Disabled Enabled]
| +| `source` _[CatalogSource](#catalogsource)_ | source allows a user to define the source of a catalog.
A "catalog" contains information on content that can be installed on a cluster.
Providing a catalog source makes the contents of the catalog discoverable and usable by
other on-cluster components.
These on-cluster components may do a variety of things with this information, such as
presenting the content in a GUI dashboard or installing content from the catalog on the cluster.
The catalog source must contain catalog metadata in the File-Based Catalog (FBC) format.
For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs.
source is a required field.

Below is a minimal example of a ClusterCatalogSpec that sources a catalog from an image:

source:
type: Image
image:
ref: quay.io/operatorhubio/catalog:latest | | Required: \{\}
| +| `priority` _integer_ | priority allows the user to define a priority for a ClusterCatalog.
priority is optional.

A ClusterCatalog's priority is used by clients as a tie-breaker between ClusterCatalogs that meet the client's requirements.
A higher number means higher priority.

It is up to clients to decide how to handle scenarios where multiple ClusterCatalogs with the same priority meet their requirements.
When deciding how to break the tie in this scenario, it is recommended that clients prompt their users for additional input.

When omitted, the default priority is 0 because that is the zero value of integers.

Negative numbers can be used to specify a priority lower than the default.
Positive numbers can be used to specify a priority higher than the default.

The lowest possible value is -2147483648.
The highest possible value is 2147483647. | 0 | | +| `availabilityMode` _[AvailabilityMode](#availabilitymode)_ | availabilityMode allows users to define how the ClusterCatalog is made available to clients on the cluster.
availabilityMode is optional.

Allowed values are "Available" and "Unavailable" and omitted.

When omitted, the default value is "Available".

When set to "Available", the catalog contents will be unpacked and served over the catalog content HTTP server.
Setting the availabilityMode to "Available" tells clients that they should consider this ClusterCatalog
and its contents as usable.

When set to "Unavailable", the catalog contents will no longer be served over the catalog content HTTP server.
When set to this availabilityMode it should be interpreted the same as the ClusterCatalog not existing.
Setting the availabilityMode to "Unavailable" can be useful in scenarios where a user may not want
to delete the ClusterCatalog all together, but would still like it to be treated as if it doesn't exist. | Available | Enum: [Unavailable Available]
| #### ClusterCatalogStatus @@ -114,10 +131,10 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterCatalog.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human-readable message that further elaborates on the state of the condition.

The current set of condition types are:
- "Serving", which represents whether or not the contents of the catalog are being served via the HTTP(S) web server.
- "Progressing", which represents whether or not the ClusterCatalog is progressing towards a new state.

The current set of reasons are:
- "Succeeded", this reason is set on the "Progressing" condition when progressing to a new state is successful.
- "Blocked", this reason is set on the "Progressing" condition when the ClusterCatalog controller has encountered an error that requires manual intervention for recovery.
- "Retrying", this reason is set on the "Progressing" condition when the ClusterCatalog controller has encountered an error that might be resolvable on subsequent reconciliation attempts.
- "Available", this reason is set on the "Serving" condition when the contents of the ClusterCatalog are being served via an endpoint on the HTTP(S) web server.
- "Unavailable", this reason is set on the "Serving" condition when there is not an endpoint on the HTTP(S) web server that is serving the contents of the ClusterCatalog. | | | -| `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source based on the source type.

Below is an example of a resolved source for an image source:
resolvedSource:

image:
lastSuccessfulPollAttempt: "2024-09-10T12:22:13Z"
ref: quay.io/operatorhubio/catalog@sha256:c7392b4be033da629f9d665fec30f6901de51ce3adebeff0af579f311ee5cf1b
type: Image | | | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterCatalog.

The current condition types are Serving and Progressing.

The Serving condition is used to represent whether or not the contents of the catalog is being served via the HTTP(S) web server.
When it has a status of True and a reason of Available, the contents of the catalog are being served.
When it has a status of False and a reason of Unavailable, the contents of the catalog are not being served because the contents are not yet available.
When it has a status of False and a reason of UserSpecifiedUnavailable, the contents of the catalog are not being served because the catalog has been intentionally marked as unavailable.

The Progressing condition is used to represent whether or not the ClusterCatalog is progressing or is ready to progress towards a new state.
When it has a status of True and a reason of Retrying, there was an error in the progression of the ClusterCatalog that may be resolved on subsequent reconciliation attempts.
When it has a status of True and a reason of Succeeded, the ClusterCatalog has successfully progressed to a new state and is ready to continue progressing.
When it has a status of False and a reason of Blocked, there was an error in the progression of the ClusterCatalog that requires manual intervention for recovery.

In the case that the Serving condition is True with reason Available and Progressing is True with reason Retrying, the previously fetched
catalog contents are still being served via the HTTP(S) web server while we are progressing towards serving a new version of the catalog
contents. This could occur when we've initially fetched the latest contents from the source for this catalog and when polling for changes
to the contents we identify that there are updates to the contents. | | | +| `resolvedSource` _[ResolvedCatalogSource](#resolvedcatalogsource)_ | resolvedSource contains information about the resolved source based on the source type. | | | | `urls` _[ClusterCatalogURLs](#clustercatalogurls)_ | urls contains the URLs that can be used to access the catalog. | | | -| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastUnpacked represents the time when the
ClusterCatalog object was last unpacked successfully. | | | +| `lastUnpacked` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastUnpacked represents the last time the contents of the
catalog were extracted from their source format. As an example,
when using an Image source, the OCI image will be pulled and the
image layers written to a file-system backed cache. We refer to the
act of this extraction from the source format as "unpacking". | | | #### ClusterCatalogURLs @@ -133,7 +150,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `base` _string_ | base is a required cluster-internal URL which provides API access for this ClusterCatalog.
A suffix API access path can be added to retrieve catalog data for the ClusterCatalog.
Currently, a 'v1' API access provides complete FBC retrival via the path "/api/v1/all", with the general form `\{base\}/api/v1/all`. | | Required: \{\}
| +| `base` _string_ | base is a cluster-internal URL that provides endpoints for
accessing the content of the catalog.

It is expected that clients append the path for the endpoint they wish
to access.

Currently, only a single endpoint is served and is accessible at the path
/api/v1.

The endpoints served for the v1 API are:
- /all - this endpoint returns the entirety of the catalog contents in the FBC format

As the needs of users and clients of the evolve, new endpoints may be added. | | MaxLength: 525
Required: \{\}
| #### ImageSource @@ -143,14 +160,18 @@ _Appears in:_ ImageSource enables users to define the information required for sourcing a Catalog from an OCI image +If we see that there is a possibly valid digest-based image reference AND pollIntervalMinutes is specified, +reject the resource since there is no use in polling a digest-based image reference. + + _Appears in:_ - [CatalogSource](#catalogsource) | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `ref` _string_ | ref is a required field that allows the user to define the reference to a container image containing Catalog contents.
Examples:
ref: quay.io/operatorhubio/catalog:latest # image reference
ref: quay.io/operatorhubio/catalog@sha256:c7392b4be033da629f9d665fec30f6901de51ce3adebeff0af579f311ee5cf1b # image reference with sha256 digest | | | -| `pollInterval` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#duration-v1-meta)_ | pollInterval is an optional field that allows the user to set the interval at which the image source should be polled for new content.
It must be specified as a duration.
It must not be specified for a catalog image referenced by a sha256 digest.
Examples:
pollInterval: 1h # poll the image source every hour
pollInterval: 30m # poll the image source every 30 minutes
pollInterval: 1h30m # poll the image source every 1 hour and 30 minutes

When omitted, the image will not be polled for new content. | | Format: duration
| +| `ref` _string_ | ref allows users to define the reference to a container image containing Catalog contents.
ref is required.
ref can not be more than 1000 characters.

A reference can be broken down into 3 parts - the domain, name, and identifier.

The domain is typically the registry where an image is located.
It must be alphanumeric characters (lowercase and uppercase) separated by the "." character.
Hyphenation is allowed, but the domain must start and end with alphanumeric characters.
Specifying a port to use is also allowed by adding the ":" character followed by numeric values.
The port must be the last value in the domain.
Some examples of valid domain values are "registry.mydomain.io", "quay.io", "my-registry.io:8080".

The name is typically the repository in the registry where an image is located.
It must contain lowercase alphanumeric characters separated only by the ".", "_", "__", "-" characters.
Multiple names can be concatenated with the "/" character.
The domain and name are combined using the "/" character.
Some examples of valid name values are "operatorhubio/catalog", "catalog", "my-catalog.prod".
An example of the domain and name parts of a reference being combined is "quay.io/operatorhubio/catalog".

The identifier is typically the tag or digest for an image reference and is present at the end of the reference.
It starts with a separator character used to distinguish the end of the name and beginning of the identifier.
For a digest-based reference, the "@" character is the separator.
For a tag-based reference, the ":" character is the separator.
An identifier is required in the reference.

Digest-based references must contain an algorithm reference immediately after the "@" separator.
The algorithm reference must be followed by the ":" character and an encoded string.
The algorithm must start with an uppercase or lowercase alpha character followed by alphanumeric characters and may contain the "-", "_", "+", and "." characters.
Some examples of valid algorithm values are "sha256", "sha256+b64u", "multihash+base58".
The encoded string following the algorithm must be hex digits (a-f, A-F, 0-9) and must be a minimum of 32 characters.

Tag-based references must begin with a word character (alphanumeric + "_") followed by word characters or ".", and "-" characters.
The tag must not be longer than 127 characters.

An example of a valid digest-based image reference is "quay.io/operatorhubio/catalog@sha256:200d4ddb2a73594b91358fe6397424e975205bfbe44614f5846033cad64b3f05"
An example of a valid tag-based image reference is "quay.io/operatorhubio/catalog:latest" | | MaxLength: 1000
Required: \{\}
| +| `pollIntervalMinutes` _integer_ | pollIntervalMinutes allows the user to set the interval, in minutes, at which the image source should be polled for new content.
pollIntervalMinutes is optional.
pollIntervalMinutes can not be specified when ref is a digest-based reference.

When omitted, the image will not be polled for new content. | | Minimum: 1
| #### ResolvedCatalogSource @@ -167,8 +188,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `type` _[SourceType](#sourcetype)_ | type is a reference to the type of source the catalog is sourced from.

It will be set to one of the following values: ["Image"].

When this field is set to "Image", information about the resolved image source will be set in the 'image' field. | | Enum: [Image]
Required: \{\}
| -| `image` _[ResolvedImageSource](#resolvedimagesource)_ | image is a field containing resolution information for a catalog sourced from an image. | | | +| `type` _[SourceType](#sourcetype)_ | type is a reference to the type of source the catalog is sourced from.
type is required.

The only allowed value is "Image".

When set to "Image", information about the resolved image source will be set in the 'image' field. | | Enum: [Image]
Required: \{\}
| +| `image` _[ResolvedImageSource](#resolvedimagesource)_ | image is a field containing resolution information for a catalog sourced from an image.
This field must be set when type is Image, and forbidden otherwise. | | | #### ResolvedImageSource @@ -184,8 +205,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `ref` _string_ | ref contains the resolved sha256 image ref containing Catalog contents. | | | -| `lastSuccessfulPollAttempt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#time-v1-meta)_ | lastSuccessfulPollAttempt is the time when the resolved source was last successfully polled for new content. | | | +| `ref` _string_ | ref contains the resolved image digest-based reference.
The digest format is used so users can use other tooling to fetch the exact
OCI manifests that were used to extract the catalog contents. | | MaxLength: 1000
Required: \{\}
| #### SourceType diff --git a/go.mod b/go.mod index 2e92a62765..28624580a2 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/onsi/gomega v1.35.1 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.35.0 + github.com/operator-framework/catalogd v0.36.0 github.com/operator-framework/helm-operator-plugins v0.7.0 github.com/operator-framework/operator-registry v1.48.0 github.com/spf13/pflag v1.0.5 @@ -26,11 +26,11 @@ require ( gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.16.2 k8s.io/api v0.31.2 - k8s.io/apiextensions-apiserver v0.31.1 + k8s.io/apiextensions-apiserver v0.31.2 k8s.io/apimachinery v0.31.2 k8s.io/cli-runtime v0.31.2 k8s.io/client-go v0.31.2 - k8s.io/component-base v0.31.1 + k8s.io/component-base v0.31.2 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 sigs.k8s.io/controller-runtime v0.19.0 @@ -241,7 +241,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.31.1 // indirect + k8s.io/apiserver v0.31.2 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/kubectl v0.31.1 // indirect oras.land/oras-go v1.2.5 // indirect diff --git a/go.sum b/go.sum index c801d4b038..b0a62ef63e 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.35.0 h1:2lQPyHIzEfcciUjQ/fo4i/GE/sX2LBzd8S+nYxlvEHU= -github.com/operator-framework/catalogd v0.35.0/go.mod h1:anZurjcFMBvbkuyqlJ98v9z+yjniPKqmhlyitk9DuBQ= +github.com/operator-framework/catalogd v0.36.0 h1:4ySpKitCooKPpTqQon/2dIGR7oEOIee8WYrRYwwQZ00= +github.com/operator-framework/catalogd v0.36.0/go.mod h1:ERq4C2ksfkf3wu3XmtGP2fIkBSqS6LfaHhtcSEcU7Ww= github.com/operator-framework/helm-operator-plugins v0.7.0 h1:YmtIWFc9BaNaDc5mk/dkG0P2BqPZOqpDvjWih5Fczuk= github.com/operator-framework/helm-operator-plugins v0.7.0/go.mod h1:fUUCJR3bWtMBZ1qdDhbwjacsBHi9uT576tF4u/DwOgQ= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= @@ -991,18 +991,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= -k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= -k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= +k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0= +k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM= k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= -k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= +k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4= +k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE= k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ= k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q= k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= -k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= -k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= +k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA= +k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= diff --git a/internal/authentication/tokengetter.go b/internal/authentication/tokengetter.go index 70e8f043e5..3fec56f371 100644 --- a/internal/authentication/tokengetter.go +++ b/internal/authentication/tokengetter.go @@ -83,7 +83,7 @@ func (t *TokenGetter) getToken(ctx context.Context, key types.NamespacedName) (* req, err := t.client.ServiceAccounts(key.Namespace).CreateToken(ctx, key.Name, &authenticationv1.TokenRequest{ - Spec: authenticationv1.TokenRequestSpec{ExpirationSeconds: ptr.To[int64](int64(t.expirationDuration / time.Second))}, + Spec: authenticationv1.TokenRequestSpec{ExpirationSeconds: ptr.To(int64(t.expirationDuration / time.Second))}, }, metav1.CreateOptions{}) if err != nil { return nil, err diff --git a/internal/resolve/catalog.go b/internal/resolve/catalog.go index de210fe3cd..78aa7b4ae8 100644 --- a/internal/resolve/catalog.go +++ b/internal/resolve/catalog.go @@ -245,7 +245,7 @@ func CatalogWalker( // Remove disabled catalogs from consideration catalogs = slices.DeleteFunc(catalogs, func(c catalogd.ClusterCatalog) bool { - if c.Spec.Availability == "Disabled" { + if c.Spec.AvailabilityMode == catalogd.AvailabilityModeUnavailable { l.Info("excluding ClusterCatalog from resolution process since it is disabled", "catalog", c.Name) return true } diff --git a/internal/resolve/catalog_test.go b/internal/resolve/catalog_test.go index 4856efc7e3..6e69c03ccd 100644 --- a/internal/resolve/catalog_test.go +++ b/internal/resolve/catalog_test.go @@ -970,12 +970,12 @@ func TestAllCatalogsDisabled(t *testing.T) { return []catalogd.ClusterCatalog{ { Spec: catalogd.ClusterCatalogSpec{ - Availability: "Disabled", + AvailabilityMode: catalogd.AvailabilityModeUnavailable, }, }, { Spec: catalogd.ClusterCatalogSpec{ - Availability: "Disabled", + AvailabilityMode: catalogd.AvailabilityModeUnavailable, }, }, }, nil @@ -1004,8 +1004,8 @@ func TestSomeCatalogsDisabled(t *testing.T) { Name: "enabledCatalog", }, Spec: catalogd.ClusterCatalogSpec{ - Priority: 1, // Higher priority - Availability: "Enabled", + Priority: 1, // Higher priority + AvailabilityMode: catalogd.AvailabilityModeAvailable, }, }, { @@ -1013,8 +1013,8 @@ func TestSomeCatalogsDisabled(t *testing.T) { Name: "disabledCatalog", }, Spec: catalogd.ClusterCatalogSpec{ - Priority: 0, // Lower priority (but disabled) - Availability: "Disabled", + Priority: 0, // Lower priority (but disabled) + AvailabilityMode: catalogd.AvailabilityModeUnavailable, }, }, }, nil diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index b292c46538..93467f5831 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -4,11 +4,11 @@ import ( "context" "os" "testing" - "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/rest" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -53,8 +53,8 @@ func createTestCatalog(ctx context.Context, name string, imageRef string) (*cata Source: catalogd.CatalogSource{ Type: catalogd.SourceTypeImage, Image: &catalogd.ImageSource{ - Ref: imageRef, - PollInterval: &metav1.Duration{Duration: time.Second}, + Ref: imageRef, + PollIntervalMinutes: ptr.To(1), }, }, }, From 26904a29b8622d4665d10b160be0a038fc04f540 Mon Sep 17 00:00:00 2001 From: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:12:25 +0000 Subject: [PATCH 106/694] fix: resolve JSONPath error in caBundle readiness check by adding kubectl_wait_for_query function (#1429) By running `make kind-deploy` against a kind cluster or installing the released script from https://operator-framework.github.io/operator-controller/getting-started/olmv1_getting_started/ the following error is faced: ```sh ... deployment.apps/cert-manager-webhook condition met deployment.apps/cert-manager-cainjector condition met deployment.apps/cert-manager condition met error: jsonpath wait format must be --for=jsonpath='{.status.readyReplicas}'=3 ``` This PR fixes an issue with kubectl wait when used to check the caBundle field in `mutatingwebhookconfigurations` and `validatingwebhookconfigurations`. This PR introduces the `kubectl_wait_for_query` function, which replaces `kubectl wait` for this specific use case. The function repeatedly checks the `caBundle` field by using `kubectl get` in a loop, ensuring that the `caBundle` is populated without relying on status-based conditions. This approach provides a more flexible solution compatible with webhook configurations, bypassing the limitations of `kubectl wait`. --- scripts/install.tpl.sh | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/scripts/install.tpl.sh b/scripts/install.tpl.sh index c1907ddc95..c3525dbcbb 100644 --- a/scripts/install.tpl.sh +++ b/scripts/install.tpl.sh @@ -41,13 +41,40 @@ function kubectl_wait_rollout() { kubectl rollout status --namespace="${namespace}" "${runtime}" --timeout="${timeout}" } +function kubectl_wait_for_query() { + manifest=$1 + query=$2 + timeout=$3 + poll_interval_in_seconds=$4 + + if [[ -z "$manifest" || -z "$query" || -z "$timeout" || -z "$poll_interval_in_seconds" ]]; then + echo "Error: Missing arguments." + echo "Usage: kubectl_wait_for_query " + exit 1 + fi + + start_time=$(date +%s) + while true; do + val=$(kubectl get "${manifest}" -o jsonpath="${query}" 2>/dev/null || echo "") + if [[ -n "${val}" ]]; then + echo "${manifest} has ${query}." + break + fi + if [[ $(( $(date +%s) - start_time )) -ge ${timeout} ]]; then + echo "Timed out waiting for ${manifest} to have ${query}." + exit 1 + fi + sleep ${poll_interval_in_seconds}s + done +} + kubectl apply -f "https://github.com/cert-manager/cert-manager/releases/download/$%7Bcert_mgr_version%7D/cert-manager.yaml" # Wait for cert-manager to be fully ready kubectl_wait "cert-manager" "deployment/cert-manager-webhook" "60s" kubectl_wait "cert-manager" "deployment/cert-manager-cainjector" "60s" kubectl_wait "cert-manager" "deployment/cert-manager" "60s" -kubectl wait mutatingwebhookconfigurations/cert-manager-webhook --for=jsonpath='{.webhooks[0].clientConfig.caBundle}' --timeout=60s -kubectl wait validatingwebhookconfigurations/cert-manager-webhook --for=jsonpath='{.webhooks[0].clientConfig.caBundle}' --timeout=60s +kubectl_wait_for_query "mutatingwebhookconfigurations/cert-manager-webhook" '{.webhooks[0].clientConfig.caBundle}' 60 5 +kubectl_wait_for_query "validatingwebhookconfigurations/cert-manager-webhook" '{.webhooks[0].clientConfig.caBundle}' 60 5 kubectl apply -f "https://github.com/operator-framework/catalogd/releases/download/$%7Bcatalogd_version%7D/catalogd.yaml" # Wait for the rollout, and then wait for the deployment to be Available From 8bccc74cde528756cf4f649d99e00f6cee3f03e3 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Thu, 7 Nov 2024 13:15:18 -0500 Subject: [PATCH 107/694] Use a separate namespace for each e2e test (#1428) Fixes #1307 Create and use a new namespace for every e2e test. This means that extension resources are placed in their own namespace. The tests deletes the namespace, and then waits until completion. This ensures that _most_ of an extension's resources are deleted. It does guarantee that global resources (e.g. CRDs, CRs, CRBs) are deleted. And would eventually allow the tests to be run in parallel (assuming the installed extensions allow for that). Signed-off-by: Todd Short --- test/e2e/cluster_extension_install_test.go | 140 +++++++++++++++------ test/e2e/e2e_suite_test.go | 2 + 2 files changed, 105 insertions(+), 37 deletions(-) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 6efe98ca83..c141ff0ca6 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -17,9 +17,11 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/errors" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/rand" kubeclient "k8s.io/client-go/kubernetes" @@ -38,6 +40,19 @@ const ( var pollDuration = time.Minute var pollInterval = time.Second +func createNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + err := c.Create(ctx, ns) + if err != nil { + return nil, err + } + return ns, nil +} + func createServiceAccount(ctx context.Context, name types.NamespacedName, clusterExtensionName string) (*corev1.ServiceAccount, error) { sa := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ @@ -177,44 +192,93 @@ func createClusterRoleAndBindingForSA(ctx context.Context, name string, sa *core return nil } -func testInit(t *testing.T) (*ocv1alpha1.ClusterExtension, *catalogd.ClusterCatalog, *corev1.ServiceAccount) { +func testInit(t *testing.T) (*ocv1alpha1.ClusterExtension, *catalogd.ClusterCatalog, *corev1.ServiceAccount, *corev1.Namespace) { var err error - extensionCatalog, err := createTestCatalog(context.Background(), testCatalogName, os.Getenv(testCatalogRefEnvVar)) - require.NoError(t, err) clusterExtensionName := fmt.Sprintf("clusterextension-%s", rand.String(8)) + + ns, err := createNamespace(context.Background(), clusterExtensionName) + require.NoError(t, err) + clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: clusterExtensionName, }, } - defaultNamespace := types.NamespacedName{ + extensionCatalog, err := createTestCatalog(context.Background(), testCatalogName, os.Getenv(testCatalogRefEnvVar)) + require.NoError(t, err) + + name := types.NamespacedName{ Name: clusterExtensionName, - Namespace: "default", + Namespace: ns.GetName(), } - sa, err := createServiceAccount(context.Background(), defaultNamespace, clusterExtensionName) + sa, err := createServiceAccount(context.Background(), name, clusterExtensionName) require.NoError(t, err) - return clusterExtension, extensionCatalog, sa + return clusterExtension, extensionCatalog, sa, ns } -func testCleanup(t *testing.T, cat *catalogd.ClusterCatalog, clusterExtension *ocv1alpha1.ClusterExtension, sa *corev1.ServiceAccount) { +func ensureNoExtensionResources(t *testing.T, clusterExtensionName string) { + ls := labels.Set{"olm.operatorframework.io/owner-name": clusterExtensionName} + + // CRDs may take an extra long time to be deleted, and may run into the following error: + // Condition=Terminating Status=True Reason=InstanceDeletionFailed Message="could not list instances: storage is (re)initializing" + t.Logf("By waiting for CustomResourceDefinitions of %q to be deleted", clusterExtensionName) + require.EventuallyWithT(t, func(ct *assert.CollectT) { + list := &apiextensionsv1.CustomResourceDefinitionList{} + err := c.List(context.Background(), list, client.MatchingLabelsSelector{Selector: ls.AsSelector()}) + assert.NoError(ct, err) + assert.Empty(ct, list.Items) + }, 5*pollDuration, pollInterval) + + t.Logf("By waiting for ClusterRoleBindings of %q to be deleted", clusterExtensionName) + require.EventuallyWithT(t, func(ct *assert.CollectT) { + list := &rbacv1.ClusterRoleBindingList{} + err := c.List(context.Background(), list, client.MatchingLabelsSelector{Selector: ls.AsSelector()}) + assert.NoError(ct, err) + assert.Empty(ct, list.Items) + }, 2*pollDuration, pollInterval) + + t.Logf("By waiting for ClusterRoles of %q to be deleted", clusterExtensionName) + require.EventuallyWithT(t, func(ct *assert.CollectT) { + list := &rbacv1.ClusterRoleList{} + err := c.List(context.Background(), list, client.MatchingLabelsSelector{Selector: ls.AsSelector()}) + assert.NoError(ct, err) + assert.Empty(ct, list.Items) + }, 2*pollDuration, pollInterval) +} + +func testCleanup(t *testing.T, cat *catalogd.ClusterCatalog, clusterExtension *ocv1alpha1.ClusterExtension, sa *corev1.ServiceAccount, ns *corev1.Namespace) { + t.Logf("By deleting ClusterCatalog %q", cat.Name) require.NoError(t, c.Delete(context.Background(), cat)) require.Eventually(t, func() bool { err := c.Get(context.Background(), types.NamespacedName{Name: cat.Name}, &catalogd.ClusterCatalog{}) return errors.IsNotFound(err) }, pollDuration, pollInterval) + + t.Logf("By deleting ClusterExtension %q", clusterExtension.Name) require.NoError(t, c.Delete(context.Background(), clusterExtension)) require.Eventually(t, func() bool { err := c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, &ocv1alpha1.ClusterExtension{}) return errors.IsNotFound(err) }, pollDuration, pollInterval) + + t.Logf("By deleting ServiceAccount %q", sa.Name) require.NoError(t, c.Delete(context.Background(), sa)) require.Eventually(t, func() bool { err := c.Get(context.Background(), types.NamespacedName{Name: sa.Name, Namespace: sa.Namespace}, &corev1.ServiceAccount{}) return errors.IsNotFound(err) }, pollDuration, pollInterval) + + ensureNoExtensionResources(t, clusterExtension.Name) + + t.Logf("By deleting Namespace %q", ns.Name) + require.NoError(t, c.Delete(context.Background(), ns)) + require.Eventually(t, func() bool { + err := c.Get(context.Background(), types.NamespacedName{Name: ns.Name}, &corev1.Namespace{}) + return errors.IsNotFound(err) + }, pollDuration, pollInterval) } func TestClusterExtensionInstallRegistry(t *testing.T) { @@ -240,8 +304,8 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { t.Log("When a cluster extension is installed from a catalog") t.Log("When the extension bundle format is registry+v1") - clusterExtension, extensionCatalog, sa := testInit(t) - defer testCleanup(t, extensionCatalog, clusterExtension, sa) + clusterExtension, extensionCatalog, sa, ns := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ @@ -255,7 +319,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { }, }, Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", + Namespace: ns.Name, ServiceAccount: ocv1alpha1.ServiceAccountReference{ Name: sa.Name, }, @@ -298,8 +362,8 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { t.Log("When a cluster extension is installed from a catalog") - clusterExtension, extensionCatalog, sa := testInit(t) - defer testCleanup(t, extensionCatalog, clusterExtension, sa) + clusterExtension, extensionCatalog, sa, ns := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ @@ -310,7 +374,7 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { }, }, Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", + Namespace: ns.Name, ServiceAccount: ocv1alpha1.ServiceAccountReference{ Name: sa.Name, }, @@ -341,8 +405,8 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { t.Log("When a cluster extension is installed from a catalog") t.Log("When resolving upgrade edges") - clusterExtension, extensionCatalog, sa := testInit(t) - defer testCleanup(t, extensionCatalog, clusterExtension, sa) + clusterExtension, extensionCatalog, sa, ns := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) t.Log("By creating an ClusterExtension at a specified version") @@ -356,7 +420,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { }, }, Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", + Namespace: ns.Name, ServiceAccount: ocv1alpha1.ServiceAccountReference{ Name: sa.Name, }, @@ -406,8 +470,8 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { t.Log("When a cluster extension is installed from a catalog") t.Log("When resolving upgrade edges") - clusterExtension, extensionCatalog, sa := testInit(t) - defer testCleanup(t, extensionCatalog, clusterExtension, sa) + clusterExtension, extensionCatalog, sa, ns := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) t.Log("By creating an ClusterExtension at a specified version") @@ -420,7 +484,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { }, }, Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", + Namespace: ns.Name, ServiceAccount: ocv1alpha1.ServiceAccountReference{ Name: sa.Name, }, @@ -457,8 +521,8 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { t.Log("When a cluster extension is installed from a catalog") t.Log("When resolving upgrade edges") - clusterExtension, extensionCatalog, sa := testInit(t) - defer testCleanup(t, extensionCatalog, clusterExtension, sa) + clusterExtension, extensionCatalog, sa, ns := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) t.Log("By creating an ClusterExtension at a specified version") @@ -471,7 +535,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { }, }, Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", + Namespace: ns.Name, ServiceAccount: ocv1alpha1.ServiceAccountReference{ Name: sa.Name, }, @@ -507,8 +571,8 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { t.Log("When a cluster extension is installed from a catalog") t.Log("It resolves again when a catalog is patched with new ImageRef") - clusterExtension, extensionCatalog, sa := testInit(t) - defer testCleanup(t, extensionCatalog, clusterExtension, sa) + clusterExtension, extensionCatalog, sa, ns := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ @@ -528,7 +592,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { }, }, Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", + Namespace: ns.Name, ServiceAccount: ocv1alpha1.ServiceAccountReference{ Name: sa.Name, }, @@ -593,9 +657,11 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { Name: clusterExtensionName, }, } - sa, err := createServiceAccount(context.Background(), types.NamespacedName{Name: clusterExtensionName, Namespace: "default"}, clusterExtensionName) + ns, err := createNamespace(context.Background(), clusterExtensionName) + require.NoError(t, err) + sa, err := createServiceAccount(context.Background(), types.NamespacedName{Name: clusterExtensionName, Namespace: ns.Name}, clusterExtensionName) require.NoError(t, err) - defer testCleanup(t, extensionCatalog, clusterExtension, sa) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ @@ -609,7 +675,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { }, }, Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", + Namespace: ns.Name, ServiceAccount: ocv1alpha1.ServiceAccountReference{ Name: sa.Name, }, @@ -657,8 +723,8 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T) { t.Log("When a cluster extension is installed from a catalog") t.Log("It resolves again when managed content is changed") - clusterExtension, extensionCatalog, sa := testInit(t) - defer testCleanup(t, extensionCatalog, clusterExtension, sa) + clusterExtension, extensionCatalog, sa, ns := testInit(t) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ @@ -672,7 +738,7 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T }, }, Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", + Namespace: ns.Name, ServiceAccount: ocv1alpha1.ServiceAccountReference{ Name: sa.Name, }, @@ -712,18 +778,18 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes t.Log("When a cluster extension is installed from a catalog") t.Log("When the extension bundle format is registry+v1") - clusterExtension, extensionCatalog, _ := testInit(t) + clusterExtension, extensionCatalog, _, ns := testInit(t) + name := rand.String(10) sa := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: "default", + Namespace: ns.Name, }, } err := c.Create(context.Background(), sa) require.NoError(t, err) - - defer testCleanup(t, extensionCatalog, clusterExtension, sa) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ @@ -737,7 +803,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes }, }, Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", + Namespace: ns.Name, ServiceAccount: ocv1alpha1.ServiceAccountReference{ Name: sa.Name, }, diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 93467f5831..e5e9378226 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -5,6 +5,7 @@ import ( "os" "testing" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/rest" @@ -32,6 +33,7 @@ func TestMain(m *testing.M) { cfg = ctrl.GetConfigOrDie() var err error + utilruntime.Must(apiextensionsv1.AddToScheme(scheme.Scheme)) c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) utilruntime.Must(err) From 662dd26f67a2ea61a6d3af786f26d70610aa5b0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:26:19 -0500 Subject: [PATCH 108/694] :seedling: Bump regex from 2024.9.11 to 2024.11.6 (#1436) Bumps [regex](https://github.com/mrabarnett/mrab-regex) from 2024.9.11 to 2024.11.6. - [Changelog](https://github.com/mrabarnett/mrab-regex/blob/hg/changelog.txt) - [Commits](https://github.com/mrabarnett/mrab-regex/compare/2024.9.11...2024.11.6) --- updated-dependencies: - dependency-name: regex dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8d436fb6dd..0d277c40fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,7 +27,7 @@ python-dateutil==2.9.0.post0 PyYAML==6.0.2 pyyaml_env_tag==0.1 readtime==3.0.0 -regex==2024.9.11 +regex==2024.11.6 requests==2.32.3 six==1.16.0 soupsieve==2.6 From bb8064332494f41589c10c12935b55d1629b9fc0 Mon Sep 17 00:00:00 2001 From: Joe Lanford Date: Thu, 7 Nov 2024 13:34:30 -0500 Subject: [PATCH 109/694] fix: put annotations in deployment's pod template (#1432) Signed-off-by: Joe Lanford --- internal/rukpak/convert/registryv1.go | 8 +- internal/rukpak/convert/registryv1_test.go | 191 +++++++++++---------- 2 files changed, 107 insertions(+), 92 deletions(-) diff --git a/internal/rukpak/convert/registryv1.go b/internal/rukpak/convert/registryv1.go index 0acdd0d6d8..e2eff3bc36 100644 --- a/internal/rukpak/convert/registryv1.go +++ b/internal/rukpak/convert/registryv1.go @@ -195,6 +195,7 @@ func Convert(in RegistryV1, installNamespace string, targetNamespaces []string) for _, depSpec := range in.CSV.Spec.InstallStrategy.StrategySpec.DeploymentSpecs { annotations := util.MergeMaps(in.CSV.Annotations, depSpec.Spec.Template.Annotations) annotations["olm.targetNamespaces"] = strings.Join(targetNamespaces, ",") + depSpec.Spec.Template.Annotations = annotations deployments = append(deployments, appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ Kind: "Deployment", @@ -202,10 +203,9 @@ func Convert(in RegistryV1, installNamespace string, targetNamespaces []string) }, ObjectMeta: metav1.ObjectMeta{ - Namespace: installNamespace, - Name: depSpec.Name, - Labels: depSpec.Label, - Annotations: annotations, + Namespace: installNamespace, + Name: depSpec.Name, + Labels: depSpec.Label, }, Spec: depSpec.Spec, }) diff --git a/internal/rukpak/convert/registryv1_test.go b/internal/rukpak/convert/registryv1_test.go index 991d5dbdde..8e9171dec2 100644 --- a/internal/rukpak/convert/registryv1_test.go +++ b/internal/rukpak/convert/registryv1_test.go @@ -2,11 +2,13 @@ package convert import ( "fmt" + "strings" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" schedulingv1 "k8s.io/api/scheduling/v1" @@ -73,7 +75,7 @@ var _ = Describe("RegistryV1 Suite", func() { Expect(plainBundle.Objects).To(HaveLen(1)) By("verifying if ns has been set correctly") - resObj := containsObject(unstructuredSvc, plainBundle.Objects) + resObj := findObjectByName(svc.Name, plainBundle.Objects) Expect(resObj).NotTo(BeNil()) Expect(resObj.GetNamespace()).To(BeEquivalentTo(installNamespace)) }) @@ -99,7 +101,7 @@ var _ = Describe("RegistryV1 Suite", func() { Expect(plainBundle.Objects).To(HaveLen(1)) By("verifying if ns has been set correctly") - resObj := containsObject(unstructuredSvc, plainBundle.Objects) + resObj := findObjectByName(svc.Name, plainBundle.Objects) Expect(resObj).NotTo(BeNil()) Expect(resObj.GetNamespace()).To(BeEquivalentTo(installNamespace)) }) @@ -157,7 +159,7 @@ var _ = Describe("RegistryV1 Suite", func() { Expect(plainBundle.Objects).To(HaveLen(1)) By("verifying if ns has been set correctly") - resObj := containsObject(unstructuredpriorityclass, plainBundle.Objects) + resObj := findObjectByName(pc.Name, plainBundle.Objects) Expect(resObj).NotTo(BeNil()) Expect(resObj.GetNamespace()).To(BeEmpty()) }) @@ -167,12 +169,13 @@ var _ = Describe("RegistryV1 Suite", func() { Context("Should generate objects successfully based on target namespaces", func() { var ( svc corev1.Service - csv v1alpha1.ClusterServiceVersion + baseCSV v1alpha1.ClusterServiceVersion watchNamespaces []string ) BeforeEach(func() { - csv = v1alpha1.ClusterServiceVersion{ + // base CSV definition that each test case will deep copy and modify + baseCSV = v1alpha1.ClusterServiceVersion{ ObjectMeta: metav1.ObjectMeta{ Name: "testCSV", Annotations: map[string]string{ @@ -180,9 +183,25 @@ var _ = Describe("RegistryV1 Suite", func() { }, }, Spec: v1alpha1.ClusterServiceVersionSpec{ - InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}}, InstallStrategy: v1alpha1.NamedInstallStrategy{ StrategySpec: v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ + { + Name: "testDeployment", + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "testContainer", + Image: "testImage", + }, + }, + }, + }, + }, + }, + }, Permissions: []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: "testServiceAccount", @@ -199,6 +218,7 @@ var _ = Describe("RegistryV1 Suite", func() { }, }, } + svc = corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "testService", @@ -208,13 +228,16 @@ var _ = Describe("RegistryV1 Suite", func() { installNamespace = "testInstallNamespace" }) - It("should convert into plain manifests successfully", func() { + It("should convert into plain manifests successfully with AllNamespaces", func() { + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}} + By("creating a registry v1 bundle") - watchNamespaces = []string{"testWatchNs1", "testWatchNs2"} + watchNamespaces = []string{""} unstructuredSvc := convertToUnstructured(svc) registryv1Bundle = RegistryV1{ PackageName: "testPkg", - CSV: csv, + CSV: *csv, Others: []unstructured.Unstructured{unstructuredSvc}, } @@ -224,41 +247,51 @@ var _ = Describe("RegistryV1 Suite", func() { By("verifying if plain bundle has required objects") Expect(plainBundle).ShouldNot(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(6)) + Expect(plainBundle.Objects).To(HaveLen(5)) + + By("verifying olm.targetNamespaces annotation in the deployment's pod template") + dep := findObjectByName("testDeployment", plainBundle.Objects) + Expect(dep).NotTo(BeNil()) + Expect(dep.(*appsv1.Deployment).Spec.Template.Annotations).To(HaveKeyWithValue("olm.targetNamespaces", strings.Join(watchNamespaces, ","))) }) - It("should convert into plain manifests successfully with single namespace", func() { - csv = v1alpha1.ClusterServiceVersion{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testCSV", - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}}, - InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategySpec: v1alpha1.StrategyDetailsDeployment{ - Permissions: []v1alpha1.StrategyDeploymentPermissions{ - { - ServiceAccountName: "testServiceAccount", - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"test"}, - Resources: []string{"pods"}, - Verbs: []string{"*"}, - }, - }, - }, - }, - }, - }, - }, + It("should convert into plain manifests successfully with MultiNamespace", func() { + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}} + + By("creating a registry v1 bundle") + watchNamespaces = []string{"testWatchNs1", "testWatchNs2"} + unstructuredSvc := convertToUnstructured(svc) + registryv1Bundle = RegistryV1{ + PackageName: "testPkg", + CSV: *csv, + Others: []unstructured.Unstructured{unstructuredSvc}, } + By("converting to plain") + plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces) + Expect(err).NotTo(HaveOccurred()) + + By("verifying if plain bundle has required objects") + Expect(plainBundle).ShouldNot(BeNil()) + Expect(plainBundle.Objects).To(HaveLen(7)) + + By("verifying olm.targetNamespaces annotation in the deployment's pod template") + dep := findObjectByName("testDeployment", plainBundle.Objects) + Expect(dep).NotTo(BeNil()) + Expect(dep.(*appsv1.Deployment).Spec.Template.Annotations).To(HaveKeyWithValue("olm.targetNamespaces", strings.Join(watchNamespaces, ","))) + }) + + It("should convert into plain manifests successfully with SingleNamespace", func() { + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}} + By("creating a registry v1 bundle") watchNamespaces = []string{"testWatchNs1"} unstructuredSvc := convertToUnstructured(svc) registryv1Bundle = RegistryV1{ PackageName: "testPkg", - CSV: csv, + CSV: *csv, Others: []unstructured.Unstructured{unstructuredSvc}, } @@ -268,41 +301,24 @@ var _ = Describe("RegistryV1 Suite", func() { By("verifying if plain bundle has required objects") Expect(plainBundle).ShouldNot(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(4)) + Expect(plainBundle.Objects).To(HaveLen(5)) + + By("verifying olm.targetNamespaces annotation in the deployment's pod template") + dep := findObjectByName("testDeployment", plainBundle.Objects) + Expect(dep).NotTo(BeNil()) + Expect(dep.(*appsv1.Deployment).Spec.Template.Annotations).To(HaveKeyWithValue("olm.targetNamespaces", strings.Join(watchNamespaces, ","))) }) It("should convert into plain manifests successfully with own namespace", func() { - csv = v1alpha1.ClusterServiceVersion{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testCSV", - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}}, - InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategySpec: v1alpha1.StrategyDetailsDeployment{ - Permissions: []v1alpha1.StrategyDeploymentPermissions{ - { - ServiceAccountName: "testServiceAccount", - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"test"}, - Resources: []string{"pods"}, - Verbs: []string{"*"}, - }, - }, - }, - }, - }, - }, - }, - } + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}} By("creating a registry v1 bundle") watchNamespaces = []string{installNamespace} unstructuredSvc := convertToUnstructured(svc) registryv1Bundle = RegistryV1{ PackageName: "testPkg", - CSV: csv, + CSV: *csv, Others: []unstructured.Unstructured{unstructuredSvc}, } @@ -312,16 +328,24 @@ var _ = Describe("RegistryV1 Suite", func() { By("verifying if plain bundle has required objects") Expect(plainBundle).ShouldNot(BeNil()) - Expect(plainBundle.Objects).To(HaveLen(4)) + Expect(plainBundle.Objects).To(HaveLen(5)) + + By("verifying olm.targetNamespaces annotation in the deployment's pod template") + dep := findObjectByName("testDeployment", plainBundle.Objects) + Expect(dep).NotTo(BeNil()) + Expect(dep.(*appsv1.Deployment).Spec.Template.Annotations).To(HaveKeyWithValue("olm.targetNamespaces", strings.Join(watchNamespaces, ","))) }) It("should error when multinamespace mode is supported with an empty string in target namespaces", func() { + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}} + By("creating a registry v1 bundle") watchNamespaces = []string{"testWatchNs1", ""} unstructuredSvc := convertToUnstructured(svc) registryv1Bundle = RegistryV1{ PackageName: "testPkg", - CSV: csv, + CSV: *csv, Others: []unstructured.Unstructured{unstructuredSvc}, } @@ -332,21 +356,15 @@ var _ = Describe("RegistryV1 Suite", func() { }) It("should error when single namespace mode is disabled with more than one target namespaces", func() { - csv = v1alpha1.ClusterServiceVersion{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testCSV", - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: false}}, - }, - } + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: false}} By("creating a registry v1 bundle") watchNamespaces = []string{"testWatchNs1", "testWatchNs2"} unstructuredSvc := convertToUnstructured(svc) registryv1Bundle = RegistryV1{ PackageName: "testPkg", - CSV: csv, + CSV: *csv, Others: []unstructured.Unstructured{unstructuredSvc}, } @@ -357,18 +375,12 @@ var _ = Describe("RegistryV1 Suite", func() { }) It("should error when all namespace mode is disabled with target namespace containing an empty string", func() { - csv = v1alpha1.ClusterServiceVersion{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testCSV", - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - InstallModes: []v1alpha1.InstallMode{ - {Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: false}, - {Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}, - {Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}, - {Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}, - }, - }, + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{ + {Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: false}, + {Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}, + {Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}, + {Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}, } By("creating a registry v1 bundle") @@ -376,7 +388,7 @@ var _ = Describe("RegistryV1 Suite", func() { unstructuredSvc := convertToUnstructured(svc) registryv1Bundle = RegistryV1{ PackageName: "testPkg", - CSV: csv, + CSV: *csv, Others: []unstructured.Unstructured{unstructuredSvc}, } @@ -387,12 +399,15 @@ var _ = Describe("RegistryV1 Suite", func() { }) It("should propagate csv annotations to chart metadata annotation", func() { + csv := baseCSV.DeepCopy() + csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}} + By("creating a registry v1 bundle") watchNamespaces = []string{"testWatchNs1", "testWatchNs2"} unstructuredSvc := convertToUnstructured(svc) registryv1Bundle = RegistryV1{ PackageName: "testPkg", - CSV: csv, + CSV: *csv, Others: []unstructured.Unstructured{unstructuredSvc}, } @@ -462,11 +477,11 @@ func convertToUnstructured(obj interface{}) unstructured.Unstructured { return unstructured.Unstructured{Object: unstructuredObj} } -func containsObject(obj unstructured.Unstructured, result []client.Object) client.Object { +func findObjectByName(name string, result []client.Object) client.Object { for _, o := range result { // Since this is a controlled env, comparing only the names is sufficient for now. // In future, compare GVKs too by ensuring its set on the unstructuredObj. - if o.GetName() == obj.GetName() { + if o.GetName() == name { return o } } From 8d535a691cc0a18b9e3e5b592e8790c99badddc1 Mon Sep 17 00:00:00 2001 From: Brett Tofel Date: Thu, 7 Nov 2024 14:01:48 -0500 Subject: [PATCH 110/694] =?UTF-8?q?=F0=9F=93=96=20[Docs]=20Document=20the?= =?UTF-8?q?=20granting=20of=20API=20access=20(#1407)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Document granting API access Signed-off-by: Brett Tofel * Addressing PR comments Signed-off-by: Brett Tofel * Addressing PR comments 2 Signed-off-by: Brett Tofel * Reorders document Signed-off-by: Brett Tofel * Improve wording on explanation around access needs Co-authored-by: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> * Improved wording on OLM not managing RBAC Co-authored-by: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> * Clarify admin and edit roles Co-authored-by: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> * Draw attention to RBAC all verbs grant Co-authored-by: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> * Clarify role vs. binding recommendations in notes Signed-off-by: Brett Tofel * Cluster extension everywhere we had operator ref Signed-off-by: Brett Tofel * Improved wordigin on rbac mention Co-authored-by: Michael Peter * Improve wording around who the users are Co-authored-by: Michael Peter * Add mkdocs ref and move to howto Signed-off-by: Brett Tofel * Clarify what admin clusterrole does Co-authored-by: Joe Lanford * Clean up what * verb allows Co-authored-by: Joe Lanford * Removes Notes section - covered in intro Signed-off-by: Brett Tofel * Reword mkdocs link text Signed-off-by: Brett Tofel * ReReword mkdocs link text Signed-off-by: Brett Tofel --------- Signed-off-by: Brett Tofel Co-authored-by: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> Co-authored-by: Michael Peter Co-authored-by: Joe Lanford --- docs/howto/how-to-grant-api-access.md | 174 ++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 175 insertions(+) create mode 100644 docs/howto/how-to-grant-api-access.md diff --git a/docs/howto/how-to-grant-api-access.md b/docs/howto/how-to-grant-api-access.md new file mode 100644 index 0000000000..e73464c434 --- /dev/null +++ b/docs/howto/how-to-grant-api-access.md @@ -0,0 +1,174 @@ + +# Granting Users Access to API Resources in OLM + +When cluster extensions are managed via OLM, they often provide Custom Resource Definitions (CRDs) that expose new API resources. Typically, cluster administrators have full management access to these resources by default, whereas non-administrative users might lack sufficient permissions. Cluster administrators must create the needed permissions to create, view, or edit these Custom Resources for these users. + +OLM v1 does **not** automatically configure or manage role-based access control (RBAC) for users to interact with the APIs provided by installed packages. Cluster administrators must manage RBAC to grant appropriate permissions to non-administrative users. This guide outlines the steps to manually configure RBAC, with a focus on creating ClusterRoles and binding them to specific users or groups. + +--- + +## 1. Finding API Groups and Resources Provided by a ClusterExtension + +To create appropriate RBAC policies, you need to know which API groups and resources are exposed by the installed cluster extension. You can inspect the installed CRDs and resources by running: + +```bash +kubectl get crds +``` + +This will list all available CRDs, and you can inspect individual CRDs for their API groups: + +```bash +kubectl get crd -o yaml +``` + +A user can use label selectors to find CRDs owned by a specific cluster extension: + +```bash +kubectl get crds -l 'olm.operatorframework.io/owner-kind=ClusterExtension,olm.operatorframework.io/owner-name=' +``` + +--- + +## 2. Creating Default ClusterRoles for API/CRD Access + +Administrators can define standard roles to control access to the API resources provided by installed cluster extensions. If the cluster extension does not provide default roles, you can create them yourself. + +### Default Roles + +- **View ClusterRole**: Grants read-only access to all custom resource objects of specified API resources across the cluster. This role is intended for users who need visibility into the resources without any permissions to modify them. It’s ideal for monitoring purposes and limited access viewing. +- **Edit ClusterRole**: Allows users to modify all custom resource objects within the cluster. This role enables users to create, update, and delete resources, making it suitable for team members who need to manage resources but should not control RBAC or manage permissions for others. +- **Admin ClusterRole**: Provides full permissions (create, update, delete) over all custom resource objects for the specified API resources across the cluster. + +### Example: Defining a Custom "View" ClusterRole + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: custom-resource-view +rules: +- apiGroups: + - + resources: + - + verbs: + - get + - list + - watch +``` + +### Example: Defining a Custom "Edit" ClusterRole + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: custom-resource-edit +rules: +- apiGroups: + - + resources: + - + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +``` + +### Example: Defining a Custom "Admin" ClusterRole + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: custom-resource-admin +rules: +- apiGroups: + - + resources: + - + verbs: + - '*' +``` +**Note**: The `'*'` in verbs allows all actions on the specified resources +In each case, replace `` and `` with the actual API group and resource names provided by the installed cluster extension. + +--- + +## 3. Granting User Access to API Resources + +Once the roles are created, you can bind them to specific users or groups to grant them the necessary permissions. There are two main ways to do this: + +### Option 1: Binding Default ClusterRoles to Users + +- **ClusterRoleBinding**: Use this to grant access across all namespaces. +- **RoleBinding**: Use this to grant access within a specific namespace. + +#### Example: ClusterRoleBinding + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: custom-resource-view-binding +subjects: +- kind: User + name: # Or use Group for group-based binding +roleRef: + kind: ClusterRole + name: custom-resource-view + apiGroup: rbac.authorization.k8s.io +``` + +This binding grants `` read-only access to the custom resource across all namespaces. + +#### Example: RoleBinding + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: custom-resource-edit-binding + namespace: +subjects: +- kind: User + name: +roleRef: + kind: Role + name: custom-resource-edit + apiGroup: rbac.authorization.k8s.io +``` + +This RoleBinding restricts permissions to a specific namespace. + +### Option 2: Extending Default Kubernetes Roles + +To automatically extend existing Kubernetes roles (e.g., the default `view`, `edit`, and `admin` roles), you can add **aggregation labels** to **ClusterRoles**. This allows users who already have `view`, `edit`, or `admin` roles to interact with the custom resource without needing additional RoleBindings. + +#### Example: Adding Aggregation Labels to a ClusterRole + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: custom-resource-aggregated-view + labels: + rbac.authorization.k8s.io/aggregate-to-view: "true" +rules: + - apiGroups: + - + resources: + - + verbs: + - get + - list + - watch +``` + +You can create similar ClusterRoles for `edit` and `admin` with appropriate verbs (such as `create`, `update`, `delete` for `edit` and `admin`). By using aggregation labels, the permissions for the custom resources are added to the default roles. + +> **Source**: [Kubernetes RBAC Aggregation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#default-roles-and-role-bindings) diff --git a/mkdocs.yml b/mkdocs.yml index 7df6b7eba9..f7b20ae070 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -41,6 +41,7 @@ nav: - Version Range Upgrades: howto/how-to-version-range-upgrades.md - Z-Stream Upgrades: howto/how-to-z-stream-upgrades.md - Derive Service Account Permissions: howto/derive-service-account.md + - Grant Access to Your Extension's API: howto/how-to-grant-api-access.md - Conceptual Guides: - Single Owner Objects: concepts/single-owner-objects.md - Upgrade Support: concepts/upgrade-support.md From ce86a659db3744a7e6690423f7314627bd3ee003 Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Thu, 7 Nov 2024 14:36:17 -0500 Subject: [PATCH 111/694] :sparkles: reduce use of carvel validators (#1269) * reduce use of carvel validators by implementing our own. This gives us more freedom in maing changes and fixing bugs in the actual validations instead of having to push all fixes up to the carvel/kapp project. Signed-off-by: everettraven * generic min/max verification functions Signed-off-by: everettraven --------- Signed-off-by: everettraven --- .../preflights/crdupgradesafety/checks.go | 237 +++++ .../crdupgradesafety/checks_test.go | 907 ++++++++++++++++++ .../crdupgradesafety/crdupgradesafety.go | 23 +- .../crdupgradesafety/crdupgradesafety_test.go | 46 +- 4 files changed, 1179 insertions(+), 34 deletions(-) create mode 100644 internal/rukpak/preflights/crdupgradesafety/checks_test.go diff --git a/internal/rukpak/preflights/crdupgradesafety/checks.go b/internal/rukpak/preflights/crdupgradesafety/checks.go index cc7be4a66d..b795b11de7 100644 --- a/internal/rukpak/preflights/crdupgradesafety/checks.go +++ b/internal/rukpak/preflights/crdupgradesafety/checks.go @@ -1,12 +1,16 @@ package crdupgradesafety import ( + "bytes" + "cmp" "errors" "fmt" + "reflect" "slices" kappcus "carvel.dev/kapp/pkg/kapp/crdupgradesafety" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/util/sets" versionhelper "k8s.io/apimachinery/pkg/version" ) @@ -70,3 +74,236 @@ func (c *ServedVersionValidator) Validate(old, new apiextensionsv1.CustomResourc func (c *ServedVersionValidator) Name() string { return "ServedVersionValidator" } + +type resetFunc func(diff kappcus.FieldDiff) kappcus.FieldDiff + +func isHandled(diff kappcus.FieldDiff, reset resetFunc) bool { + diff = reset(diff) + return reflect.DeepEqual(diff.Old, diff.New) +} + +func Enum(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.Enum = []apiextensionsv1.JSON{} + diff.New.Enum = []apiextensionsv1.JSON{} + return diff + } + + oldEnums := sets.New[string]() + for _, json := range diff.Old.Enum { + oldEnums.Insert(string(json.Raw)) + } + + newEnums := sets.New[string]() + for _, json := range diff.New.Enum { + newEnums.Insert(string(json.Raw)) + } + diffEnums := oldEnums.Difference(newEnums) + var err error + + switch { + case oldEnums.Len() == 0 && newEnums.Len() > 0: + err = fmt.Errorf("enum constraints %v added when there were no restrictions previously", newEnums.UnsortedList()) + case diffEnums.Len() > 0: + err = fmt.Errorf("enums %v removed from the set of previously allowed values", diffEnums.UnsortedList()) + } + + return isHandled(diff, reset), err +} + +func Required(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.Required = []string{} + diff.New.Required = []string{} + return diff + } + + oldRequired := sets.New(diff.Old.Required...) + newRequired := sets.New(diff.New.Required...) + diffRequired := newRequired.Difference(oldRequired) + var err error + + if diffRequired.Len() > 0 { + err = fmt.Errorf("new required fields %v added", diffRequired.UnsortedList()) + } + + return isHandled(diff, reset), err +} + +func maxVerification[T cmp.Ordered](older *T, newer *T) error { + var err error + switch { + case older == nil && newer != nil: + err = fmt.Errorf("constraint %v added when there were no restrictions previously", *newer) + case older != nil && newer != nil && *newer < *older: + err = fmt.Errorf("constraint decreased from %v to %v", *older, *newer) + } + return err +} + +func Maximum(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.Maximum = nil + diff.New.Maximum = nil + return diff + } + + err := maxVerification(diff.Old.Maximum, diff.New.Maximum) + if err != nil { + err = fmt.Errorf("maximum: %s", err.Error()) + } + + return isHandled(diff, reset), err +} + +func MaxItems(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.MaxItems = nil + diff.New.MaxItems = nil + return diff + } + + err := maxVerification(diff.Old.MaxItems, diff.New.MaxItems) + if err != nil { + err = fmt.Errorf("maxItems: %s", err.Error()) + } + + return isHandled(diff, reset), err +} + +func MaxLength(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.MaxLength = nil + diff.New.MaxLength = nil + return diff + } + + err := maxVerification(diff.Old.MaxLength, diff.New.MaxLength) + if err != nil { + err = fmt.Errorf("maxLength: %s", err.Error()) + } + + return isHandled(diff, reset), err +} + +func MaxProperties(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.MaxProperties = nil + diff.New.MaxProperties = nil + return diff + } + + err := maxVerification(diff.Old.MaxProperties, diff.New.MaxProperties) + if err != nil { + err = fmt.Errorf("maxProperties: %s", err.Error()) + } + + return isHandled(diff, reset), err +} + +func minVerification[T cmp.Ordered](older *T, newer *T) error { + var err error + switch { + case older == nil && newer != nil: + err = fmt.Errorf("constraint %v added when there were no restrictions previously", *newer) + case older != nil && newer != nil && *newer > *older: + err = fmt.Errorf("constraint increased from %v to %v", *older, *newer) + } + return err +} + +func Minimum(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.Minimum = nil + diff.New.Minimum = nil + return diff + } + + err := minVerification(diff.Old.Minimum, diff.New.Minimum) + if err != nil { + err = fmt.Errorf("minimum: %s", err.Error()) + } + + return isHandled(diff, reset), err +} + +func MinItems(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.MinItems = nil + diff.New.MinItems = nil + return diff + } + + err := minVerification(diff.Old.MinItems, diff.New.MinItems) + if err != nil { + err = fmt.Errorf("minItems: %s", err.Error()) + } + + return isHandled(diff, reset), err +} + +func MinLength(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.MinLength = nil + diff.New.MinLength = nil + return diff + } + + err := minVerification(diff.Old.MinLength, diff.New.MinLength) + if err != nil { + err = fmt.Errorf("minLength: %s", err.Error()) + } + + return isHandled(diff, reset), err +} + +func MinProperties(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.MinProperties = nil + diff.New.MinProperties = nil + return diff + } + + err := minVerification(diff.Old.MinProperties, diff.New.MinProperties) + if err != nil { + err = fmt.Errorf("minProperties: %s", err.Error()) + } + + return isHandled(diff, reset), err +} + +func Default(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.Default = nil + diff.New.Default = nil + return diff + } + + var err error + + switch { + case diff.Old.Default == nil && diff.New.Default != nil: + err = fmt.Errorf("default value %q added when there was no default previously", string(diff.New.Default.Raw)) + case diff.Old.Default != nil && diff.New.Default == nil: + err = fmt.Errorf("default value %q removed", string(diff.Old.Default.Raw)) + case diff.Old.Default != nil && diff.New.Default != nil && !bytes.Equal(diff.Old.Default.Raw, diff.New.Default.Raw): + err = fmt.Errorf("default value changed from %q to %q", string(diff.Old.Default.Raw), string(diff.New.Default.Raw)) + } + + return isHandled(diff, reset), err +} + +func Type(diff kappcus.FieldDiff) (bool, error) { + reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff { + diff.Old.Type = "" + diff.New.Type = "" + return diff + } + + var err error + if diff.Old.Type != diff.New.Type { + err = fmt.Errorf("type changed from %q to %q", diff.Old.Type, diff.New.Type) + } + + return isHandled(diff, reset), err +} diff --git a/internal/rukpak/preflights/crdupgradesafety/checks_test.go b/internal/rukpak/preflights/crdupgradesafety/checks_test.go new file mode 100644 index 0000000000..6544006ce4 --- /dev/null +++ b/internal/rukpak/preflights/crdupgradesafety/checks_test.go @@ -0,0 +1,907 @@ +package crdupgradesafety + +import ( + "errors" + "testing" + + kappcus "carvel.dev/kapp/pkg/kapp/crdupgradesafety" + "github.com/stretchr/testify/require" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/utils/ptr" +) + +type testcase struct { + name string + diff kappcus.FieldDiff + err error + handled bool +} + +func TestEnum(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Enum: []apiextensionsv1.JSON{ + { + Raw: []byte("foo"), + }, + }, + }, + New: &apiextensionsv1.JSONSchemaProps{ + Enum: []apiextensionsv1.JSON{ + { + Raw: []byte("foo"), + }, + }, + }, + }, + err: nil, + handled: true, + }, + { + name: "new enum constraint, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Enum: []apiextensionsv1.JSON{}, + }, + New: &apiextensionsv1.JSONSchemaProps{ + Enum: []apiextensionsv1.JSON{ + { + Raw: []byte("foo"), + }, + }, + }, + }, + err: errors.New("enum constraints [foo] added when there were no restrictions previously"), + handled: true, + }, + { + name: "remove enum value, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Enum: []apiextensionsv1.JSON{ + { + Raw: []byte("foo"), + }, + { + Raw: []byte("bar"), + }, + }, + }, + New: &apiextensionsv1.JSONSchemaProps{ + Enum: []apiextensionsv1.JSON{ + { + Raw: []byte("bar"), + }, + }, + }, + }, + err: errors.New("enums [foo] removed from the set of previously allowed values"), + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + { + name: "different field changed with enum, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + Enum: []apiextensionsv1.JSON{ + { + Raw: []byte("foo"), + }, + }, + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + Enum: []apiextensionsv1.JSON{ + { + Raw: []byte("foo"), + }, + }, + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := Enum(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestRequired(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Required: []string{ + "foo", + }, + }, + New: &apiextensionsv1.JSONSchemaProps{ + Required: []string{ + "foo", + }, + }, + }, + err: nil, + handled: true, + }, + { + name: "new required field, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + Required: []string{ + "foo", + }, + }, + }, + err: errors.New("new required fields [foo] added"), + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := Required(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestMaximum(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Maximum: ptr.To(10.0), + }, + New: &apiextensionsv1.JSONSchemaProps{ + Maximum: ptr.To(10.0), + }, + }, + err: nil, + handled: true, + }, + { + name: "new maximum constraint, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + Maximum: ptr.To(10.0), + }, + }, + err: errors.New("maximum: constraint 10 added when there were no restrictions previously"), + handled: true, + }, + { + name: "maximum constraint decreased, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Maximum: ptr.To(20.0), + }, + New: &apiextensionsv1.JSONSchemaProps{ + Maximum: ptr.To(10.0), + }, + }, + err: errors.New("maximum: constraint decreased from 20 to 10"), + handled: true, + }, + { + name: "maximum constraint increased, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Maximum: ptr.To(20.0), + }, + New: &apiextensionsv1.JSONSchemaProps{ + Maximum: ptr.To(30.0), + }, + }, + err: nil, + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := Maximum(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestMaxItems(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MaxItems: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MaxItems: ptr.To(int64(10)), + }, + }, + err: nil, + handled: true, + }, + { + name: "new maxItems constraint, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + MaxItems: ptr.To(int64(10)), + }, + }, + err: errors.New("maxItems: constraint 10 added when there were no restrictions previously"), + handled: true, + }, + { + name: "maxItems constraint decreased, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MaxItems: ptr.To(int64(20)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MaxItems: ptr.To(int64(10)), + }, + }, + err: errors.New("maxItems: constraint decreased from 20 to 10"), + handled: true, + }, + { + name: "maxitems constraint increased, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MaxItems: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MaxItems: ptr.To(int64(20)), + }, + }, + err: nil, + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := MaxItems(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestMaxLength(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MaxLength: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MaxLength: ptr.To(int64(10)), + }, + }, + err: nil, + handled: true, + }, + { + name: "new maxLength constraint, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + MaxLength: ptr.To(int64(10)), + }, + }, + err: errors.New("maxLength: constraint 10 added when there were no restrictions previously"), + handled: true, + }, + { + name: "maxLength constraint decreased, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MaxLength: ptr.To(int64(20)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MaxLength: ptr.To(int64(10)), + }, + }, + err: errors.New("maxLength: constraint decreased from 20 to 10"), + handled: true, + }, + { + name: "maxLength constraint increased, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MaxLength: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MaxLength: ptr.To(int64(20)), + }, + }, + err: nil, + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := MaxLength(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestMaxProperties(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MaxProperties: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MaxProperties: ptr.To(int64(10)), + }, + }, + err: nil, + handled: true, + }, + { + name: "new maxProperties constraint, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + MaxProperties: ptr.To(int64(10)), + }, + }, + err: errors.New("maxProperties: constraint 10 added when there were no restrictions previously"), + handled: true, + }, + { + name: "maxProperties constraint decreased, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MaxProperties: ptr.To(int64(20)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MaxProperties: ptr.To(int64(10)), + }, + }, + err: errors.New("maxProperties: constraint decreased from 20 to 10"), + handled: true, + }, + { + name: "maxProperties constraint increased, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MaxProperties: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MaxProperties: ptr.To(int64(20)), + }, + }, + err: nil, + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := MaxProperties(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestMinItems(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MinItems: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MinItems: ptr.To(int64(10)), + }, + }, + err: nil, + handled: true, + }, + { + name: "new minItems constraint, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + MinItems: ptr.To(int64(10)), + }, + }, + err: errors.New("minItems: constraint 10 added when there were no restrictions previously"), + handled: true, + }, + { + name: "minItems constraint decreased, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MinItems: ptr.To(int64(20)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MinItems: ptr.To(int64(10)), + }, + }, + err: nil, + handled: true, + }, + { + name: "minItems constraint increased, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MinItems: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MinItems: ptr.To(int64(20)), + }, + }, + err: errors.New("minItems: constraint increased from 10 to 20"), + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := MinItems(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestMinimum(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Minimum: ptr.To(10.0), + }, + New: &apiextensionsv1.JSONSchemaProps{ + Minimum: ptr.To(10.0), + }, + }, + err: nil, + handled: true, + }, + { + name: "new minimum constraint, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + Minimum: ptr.To(10.0), + }, + }, + err: errors.New("minimum: constraint 10 added when there were no restrictions previously"), + handled: true, + }, + { + name: "minLength constraint decreased, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Minimum: ptr.To(20.0), + }, + New: &apiextensionsv1.JSONSchemaProps{ + Minimum: ptr.To(10.0), + }, + }, + err: nil, + handled: true, + }, + { + name: "minLength constraint increased, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Minimum: ptr.To(10.0), + }, + New: &apiextensionsv1.JSONSchemaProps{ + Minimum: ptr.To(20.0), + }, + }, + err: errors.New("minimum: constraint increased from 10 to 20"), + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := Minimum(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestMinLength(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MinLength: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MinLength: ptr.To(int64(10)), + }, + }, + err: nil, + handled: true, + }, + { + name: "new minLength constraint, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + MinLength: ptr.To(int64(10)), + }, + }, + err: errors.New("minLength: constraint 10 added when there were no restrictions previously"), + handled: true, + }, + { + name: "minLength constraint decreased, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MinLength: ptr.To(int64(20)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MinLength: ptr.To(int64(10)), + }, + }, + err: nil, + handled: true, + }, + { + name: "minLength constraint increased, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MinLength: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MinLength: ptr.To(int64(20)), + }, + }, + err: errors.New("minLength: constraint increased from 10 to 20"), + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := MinLength(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestMinProperties(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MinProperties: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MinProperties: ptr.To(int64(10)), + }, + }, + err: nil, + handled: true, + }, + { + name: "new minProperties constraint, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + MinProperties: ptr.To(int64(10)), + }, + }, + err: errors.New("minProperties: constraint 10 added when there were no restrictions previously"), + handled: true, + }, + { + name: "minProperties constraint decreased, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MinProperties: ptr.To(int64(20)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MinProperties: ptr.To(int64(10)), + }, + }, + err: nil, + handled: true, + }, + { + name: "minProperties constraint increased, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + MinProperties: ptr.To(int64(10)), + }, + New: &apiextensionsv1.JSONSchemaProps{ + MinProperties: ptr.To(int64(20)), + }, + }, + err: errors.New("minProperties: constraint increased from 10 to 20"), + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := MinProperties(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestDefault(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Default: &apiextensionsv1.JSON{ + Raw: []byte("foo"), + }, + }, + New: &apiextensionsv1.JSONSchemaProps{ + Default: &apiextensionsv1.JSON{ + Raw: []byte("foo"), + }, + }, + }, + err: nil, + handled: true, + }, + { + name: "new default value, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{}, + New: &apiextensionsv1.JSONSchemaProps{ + Default: &apiextensionsv1.JSON{ + Raw: []byte("foo"), + }, + }, + }, + err: errors.New("default value \"foo\" added when there was no default previously"), + handled: true, + }, + { + name: "default value removed, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Default: &apiextensionsv1.JSON{ + Raw: []byte("foo"), + }, + }, + New: &apiextensionsv1.JSONSchemaProps{}, + }, + err: errors.New("default value \"foo\" removed"), + handled: true, + }, + { + name: "default value changed, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Default: &apiextensionsv1.JSON{ + Raw: []byte("foo"), + }, + }, + New: &apiextensionsv1.JSONSchemaProps{ + Default: &apiextensionsv1.JSON{ + Raw: []byte("bar"), + }, + }, + }, + err: errors.New("default value changed from \"foo\" to \"bar\""), + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := Default(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} + +func TestType(t *testing.T) { + for _, tc := range []testcase{ + { + name: "no diff, no error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Type: "string", + }, + New: &apiextensionsv1.JSONSchemaProps{ + Type: "string", + }, + }, + err: nil, + handled: true, + }, + { + name: "type changed, error, handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + Type: "string", + }, + New: &apiextensionsv1.JSONSchemaProps{ + Type: "integer", + }, + }, + err: errors.New("type changed from \"string\" to \"integer\""), + handled: true, + }, + { + name: "different field changed, no error, not handled", + diff: kappcus.FieldDiff{ + Old: &apiextensionsv1.JSONSchemaProps{ + ID: "foo", + }, + New: &apiextensionsv1.JSONSchemaProps{ + ID: "bar", + }, + }, + err: nil, + handled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + handled, err := Type(tc.diff) + require.Equal(t, tc.err, err) + require.Equal(t, tc.handled, handled) + }) + } +} diff --git a/internal/rukpak/preflights/crdupgradesafety/crdupgradesafety.go b/internal/rukpak/preflights/crdupgradesafety/crdupgradesafety.go index 3f91c8c2b4..7cdd905f66 100644 --- a/internal/rukpak/preflights/crdupgradesafety/crdupgradesafety.go +++ b/internal/rukpak/preflights/crdupgradesafety/crdupgradesafety.go @@ -32,17 +32,18 @@ type Preflight struct { func NewPreflight(crdCli apiextensionsv1client.CustomResourceDefinitionInterface, opts ...Option) *Preflight { changeValidations := []kappcus.ChangeValidation{ - kappcus.EnumChangeValidation, - kappcus.RequiredFieldChangeValidation, - kappcus.MaximumChangeValidation, - kappcus.MaximumItemsChangeValidation, - kappcus.MaximumLengthChangeValidation, - kappcus.MaximumPropertiesChangeValidation, - kappcus.MinimumChangeValidation, - kappcus.MinimumItemsChangeValidation, - kappcus.MinimumLengthChangeValidation, - kappcus.MinimumPropertiesChangeValidation, - kappcus.DefaultValueChangeValidation, + Enum, + Required, + Maximum, + MaxItems, + MaxLength, + MaxProperties, + Minimum, + MinItems, + MinLength, + MinProperties, + Default, + Type, } p := &Preflight{ crdClient: crdCli, diff --git a/internal/rukpak/preflights/crdupgradesafety/crdupgradesafety_test.go b/internal/rukpak/preflights/crdupgradesafety/crdupgradesafety_test.go index 39e0a0fe94..98b2289bde 100644 --- a/internal/rukpak/preflights/crdupgradesafety/crdupgradesafety_test.go +++ b/internal/rukpak/preflights/crdupgradesafety/crdupgradesafety_test.go @@ -166,17 +166,17 @@ func TestInstall(t *testing.T) { wantErrMsgs: []string{ `"NoScopeChange"`, `"NoStoredVersionRemoved"`, - `enums added`, - `new required fields added`, - `maximum constraint added when one did not exist previously`, - `maximum items constraint added`, - `maximum length constraint added`, - `maximum properties constraint added`, - `minimum constraint added when one did not exist previously`, - `minimum items constraint added`, - `minimum length constraint added`, - `minimum properties constraint added`, - `new value added as default`, + `enum constraints`, + `new required fields`, + `maximum: constraint`, + `maxItems: constraint`, + `maxLength: constraint`, + `maxProperties: constraint`, + `minimum: constraint`, + `minItems: constraint`, + `minLength: constraint`, + `minProperties: constraint`, + `default value`, }, }, { @@ -303,17 +303,17 @@ func TestUpgrade(t *testing.T) { wantErrMsgs: []string{ `"NoScopeChange"`, `"NoStoredVersionRemoved"`, - `enums added`, - `new required fields added`, - `maximum constraint added when one did not exist previously`, - `maximum items constraint added`, - `maximum length constraint added`, - `maximum properties constraint added`, - `minimum constraint added when one did not exist previously`, - `minimum items constraint added`, - `minimum length constraint added`, - `minimum properties constraint added`, - `new value added as default`, + `enum constraints`, + `new required fields`, + `maximum: constraint`, + `maxItems: constraint`, + `maxLength: constraint`, + `maxProperties: constraint`, + `minimum: constraint`, + `minItems: constraint`, + `minLength: constraint`, + `minProperties: constraint`, + `default value`, }, }, { @@ -345,7 +345,7 @@ func TestUpgrade(t *testing.T) { Manifest: getManifestString(t, "crd-conversion-no-webhook.json"), }, wantErrMsgs: []string{ - `"ServedVersionValidator" validation failed: version upgrade "v1" to "v2", field "^.spec.foobarbaz": enum values removed`, + `"ServedVersionValidator" validation failed: version upgrade "v1" to "v2", field "^.spec.foobarbaz": enums`, }, }, } From d0865da67a04041e273913761309fc3a885e3368 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:38:16 +0100 Subject: [PATCH 112/694] :seedling: Bump packaging from 24.1 to 24.2 (#1438) Bumps [packaging](https://github.com/pypa/packaging) from 24.1 to 24.2. - [Release notes](https://github.com/pypa/packaging/releases) - [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pypa/packaging/compare/24.1...24.2) --- updated-dependencies: - dependency-name: packaging dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0d277c40fa..2fd8b6c756 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ mergedeep==1.3.4 mkdocs==1.6.1 mkdocs-material==9.5.44 mkdocs-material-extensions==1.3.1 -packaging==24.1 +packaging==24.2 paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 From 6c2be08c2ae6c5509af8dbc3de126aa6c2769f54 Mon Sep 17 00:00:00 2001 From: Jordan Keister Date: Fri, 8 Nov 2024 16:03:04 -0600 Subject: [PATCH 113/694] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20updates=20from=20a?= =?UTF-8?q?pi=20audit=20(#1404)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * updates from api audit Signed-off-by: Jordan Keister * review updates Signed-off-by: Jordan Keister * e2e fixes Signed-off-by: Jordan Keister --------- Signed-off-by: Jordan Keister --- api/v1alpha1/clusterextension_types.go | 306 +++++++++--------- api/v1alpha1/zz_generated.deepcopy.go | 16 +- ...peratorframework.io_clusterextensions.yaml | 213 ++++++------ .../operator-controller-api-reference.md | 60 ++-- internal/applier/helm.go | 29 +- internal/conditionsets/conditionsets.go | 24 +- .../clusterextension_admission_test.go | 10 +- .../clusterextension_controller_test.go | 2 +- internal/controllers/common_controller.go | 3 +- .../controllers/common_controller_test.go | 4 +- internal/resolve/catalog.go | 19 +- internal/resolve/catalog_test.go | 14 +- test/e2e/cluster_extension_install_test.go | 36 +-- 13 files changed, 386 insertions(+), 350 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index 78608ba3a6..c0fba00981 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -18,15 +18,13 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/operator-framework/operator-controller/internal/conditionsets" ) var ClusterExtensionKind = "ClusterExtension" type ( - UpgradeConstraintPolicy string - CRDUpgradeSafetyPolicy string + UpgradeConstraintPolicy string + CRDUpgradeSafetyEnforcement string ) const ( @@ -58,6 +56,7 @@ type ClusterExtensionSpec struct { // catalog: // packageName: example-package // + // +kubebuilder:validation:Required Source SourceConfig `json:"source"` // install is a required field used to configure the installation options @@ -69,100 +68,93 @@ type ClusterExtensionSpec struct { // namespace: example-namespace // serviceAccount: // name: example-sa + // + // +kubebuilder:validation:Required Install ClusterExtensionInstallConfig `json:"install"` } const SourceTypeCatalog = "Catalog" // SourceConfig is a discriminated union which selects the installation source. +// // +union -// +kubebuilder:validation:XValidation:rule="self.sourceType == 'Catalog' && has(self.catalog)",message="sourceType Catalog requires catalog field" +// +kubebuilder:validation:XValidation:rule="has(self.sourceType) && self.sourceType == 'Catalog' ? has(self.catalog) : !has(self.catalog)",message="catalog is required when sourceType is Catalog, and forbidden otherwise" type SourceConfig struct { // sourceType is a required reference to the type of install source. // - // Allowed values are ["Catalog"] + // Allowed values are "Catalog" // - // When this field is set to "Catalog", information for determining the appropriate - // bundle of content to install will be fetched from ClusterCatalog resources existing - // on the cluster. When using the Catalog sourceType, the catalog field must also be set. + // When this field is set to "Catalog", information for determining the + // appropriate bundle of content to install will be fetched from + // ClusterCatalog resources existing on the cluster. + // When using the Catalog sourceType, the catalog field must also be set. // // +unionDiscriminator // +kubebuilder:validation:Enum:="Catalog" + // +kubebuilder:validation:Required SourceType string `json:"sourceType"` - // catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog", - // and must be the only field defined for this sourceType. + // catalog is used to configure how information is sourced from a catalog. + // This field is required when sourceType is "Catalog", and forbidden otherwise. // - // +optional. + // +optional Catalog *CatalogSource `json:"catalog,omitempty"` } // ClusterExtensionInstallConfig is a union which selects the clusterExtension installation config. // ClusterExtensionInstallConfig requires the namespace and serviceAccount which should be used for the installation of packages. +// // +union type ClusterExtensionInstallConfig struct { - // namespace is a reference to the Namespace in which the bundle of - // content for the package referenced in the packageName field will be applied. + // namespace designates the kubernetes Namespace where bundle content + // for the package, referenced in the 'packageName' field, will be applied and the necessary + // service account can be found. // The bundle may contain cluster-scoped resources or resources that are // applied to other Namespaces. This Namespace is expected to exist. // // namespace is required, immutable, and follows the DNS label standard - // as defined in [RFC 1123]. This means that valid values: - // - Contain no more than 63 characters - // - Contain only lowercase alphanumeric characters or '-' - // - Start with an alphanumeric character - // - End with an alphanumeric character - // - // Some examples of valid values are: - // - some-namespace - // - 123-namespace - // - 1-namespace-2 - // - somenamespace - // - // Some examples of invalid values are: - // - -some-namespace - // - some-namespace- - // - thisisareallylongnamespacenamethatisgreaterthanthemaximumlength - // - some.namespace + // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-), + // start and end with an alphanumeric character, and be no longer than 63 characters // // [RFC 1123]: https://tools.ietf.org/html/rfc1123 // - //+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - //+kubebuilder:validation:MaxLength:=63 - //+kubebuilder:validation:XValidation:rule="self == oldSelf",message="namespace is immutable" + // +kubebuilder:validation:MaxLength:=63 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="namespace is immutable" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$\")",message="namespace must be a valid DNS1123 label. It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character, and be no longer than 63 characters" + // +kubebuilder:validation:Required Namespace string `json:"namespace"` // serviceAccount is a required reference to a ServiceAccount that exists - // in the installNamespace. The provided ServiceAccount is used to install and + // in the installNamespace which is used to install and // manage the content for the package specified in the packageName field. // // In order to successfully install and manage the content for the package, // the ServiceAccount provided via this field should be configured with the // appropriate permissions to perform the necessary operations on all the // resources that are included in the bundle of content being applied. + // + // +kubebuilder:validation:Required ServiceAccount ServiceAccountReference `json:"serviceAccount"` - // preflight is an optional field that can be used to configure the preflight checks run before installation or upgrade of the content for the package specified in the packageName field. - // - // When specified, it overrides the default configuration of the preflight checks that are required to execute successfully during an install/upgrade operation. + // preflight is an optional field that can be used to configure the checks that are + // run before installation or upgrade of the content for the package specified in the packageName field. // - // When not specified, the default configuration for each preflight check will be used. + // When specified, it replaces the default preflight configuration for install/upgrade actions. + // When not specified, the default configuration will be used. // - //+optional + // +optional Preflight *PreflightConfig `json:"preflight,omitempty"` } -// CatalogSource defines the required fields for catalog source. +// CatalogSource defines the attributes used to identify and filter content from a catalog. type CatalogSource struct { // packageName is a reference to the name of the package to be installed // and is used to filter the content from catalogs. // - // This field is required, immutable and follows the DNS subdomain name - // standard as defined in [RFC 1123]. This means that valid entries: - // - Contain no more than 253 characters - // - Contain only lowercase alphanumeric characters, '-', or '.' - // - Start with an alphanumeric character - // - End with an alphanumeric character + // packageName is required, immutable, and follows the DNS subdomain standard + // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + // hyphens (-) or periods (.), start and end with an alphanumeric character, + // and be no longer than 253 characters. // // Some examples of valid values are: // - some-package @@ -178,9 +170,11 @@ type CatalogSource struct { // // [RFC 1123]: https://tools.ietf.org/html/rfc1123 // - //+kubebuilder:validation:MaxLength:=253 - //+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - //+kubebuilder:validation:XValidation:rule="self == oldSelf",message="packageName is immutable" + // +kubebuilder:validation.Required + // +kubebuilder:validation:MaxLength:=253 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="packageName is immutable" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="packageName must be a valid DNS1123 subdomain. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters" + // +kubebuilder:validation:Required PackageName string `json:"packageName"` // version is an optional semver constraint (a specific version or range of versions). When unspecified, the latest version available will be installed. @@ -257,15 +251,20 @@ type CatalogSource struct { // // For more information on semver, please see https://semver.org/ // - //+kubebuilder:validation:MaxLength:=64 - //+kubebuilder:validation:Pattern=`^(\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\^)\s*(v?(0|[1-9]\d*|[x|X|\*])(\.(0|[1-9]\d*|x|X|\*]))?(\.(0|[1-9]\d*|x|X|\*))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)((?:\s+|,\s*|\s*\|\|\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\^)\s*(v?(0|[1-9]\d*|x|X|\*])(\.(0|[1-9]\d*|x|X|\*))?(\.(0|[1-9]\d*|x|X|\*]))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)*$` - //+optional + // +kubebuilder:validation:MaxLength:=64 + // +kubebuilder:validation:XValidation:rule="self.matches(\"^(\\\\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\\\\^)\\\\s*(v?(0|[1-9]\\\\d*|[x|X|\\\\*])(\\\\.(0|[1-9]\\\\d*|x|X|\\\\*]))?(\\\\.(0|[1-9]\\\\d*|x|X|\\\\*))?(-([0-9A-Za-z\\\\-]+(\\\\.[0-9A-Za-z\\\\-]+)*))?(\\\\+([0-9A-Za-z\\\\-]+(\\\\.[0-9A-Za-z\\\\-]+)*))?)\\\\s*)((?:\\\\s+|,\\\\s*|\\\\s*\\\\|\\\\|\\\\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\\\\^)\\\\s*(v?(0|[1-9]\\\\d*|x|X|\\\\*])(\\\\.(0|[1-9]\\\\d*|x|X|\\\\*))?(\\\\.(0|[1-9]\\\\d*|x|X|\\\\*]))?(-([0-9A-Za-z\\\\-]+(\\\\.[0-9A-Za-z\\\\-]+)*))?(\\\\+([0-9A-Za-z\\\\-]+(\\\\.[0-9A-Za-z\\\\-]+)*))?)\\\\s*)*$\")",message="invalid version expression" + // +optional Version string `json:"version,omitempty"` // channels is an optional reference to a set of channels belonging to // the package specified in the packageName field. // - // A "channel" is a package author defined stream of updates for an extension. + // A "channel" is a package-author-defined stream of updates for an extension. + // + // Each channel in the list must follow the DNS subdomain standard + // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + // hyphens (-) or periods (.), start and end with an alphanumeric character, + // and be no longer than 253 characters. No more than 256 channels can be specified. // // When specified, it is used to constrain the set of installable bundles and // the automated upgrade path. This constraint is an AND operation with the @@ -277,13 +276,6 @@ type CatalogSource struct { // // When unspecified, upgrade edges across all channels will be used to identify valid automatic upgrade paths. // - // This field follows the DNS subdomain name standard as defined in [RFC - // 1123]. This means that valid entries: - // - Contain no more than 253 characters - // - Contain only lowercase alphanumeric characters, '-', or '.' - // - Start with an alphanumeric character - // - End with an alphanumeric character - // // Some examples of valid values are: // - 1.1.x // - alpha @@ -303,9 +295,10 @@ type CatalogSource struct { // // [RFC 1123]: https://tools.ietf.org/html/rfc1123 // - //+kubebuilder:validation:items:MaxLength:=253 - //+kubebuilder:validation:items:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - //+optional + // +kubebuilder:validation:items:MaxLength:=253 + // +kubebuilder:validation:MaxItems:=256 + // +kubebuilder:validation:items:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="channels entries must be valid DNS1123 subdomains" + // +optional Channels []string `json:"channels,omitempty"` // selector is an optional field that can be used @@ -315,14 +308,14 @@ type CatalogSource struct { // When unspecified, all ClusterCatalogs will be used in // the bundle selection process. // - //+optional - Selector metav1.LabelSelector `json:"selector,omitempty"` + // +optional + Selector *metav1.LabelSelector `json:"selector,omitempty"` // upgradeConstraintPolicy is an optional field that controls whether // the upgrade path(s) defined in the catalog are enforced for the package // referenced in the packageName field. // - // Allowed values are: ["CatalogProvided", "SelfCertified"]. + // Allowed values are: "CatalogProvided" or "SelfCertified", or omitted. // // When this field is set to "CatalogProvided", automatic upgrades will only occur // when upgrade constraints specified by the package author are met. @@ -334,28 +327,26 @@ type CatalogSource struct { // loss. It is assumed that users have independently verified changes when // using this option. // - // If unspecified, the default value is "CatalogProvided". + // When this field is omitted, the default value is "CatalogProvided". // - //+kubebuilder:validation:Enum:=CatalogProvided;SelfCertified - //+kubebuilder:default:=CatalogProvided - //+optional + // +kubebuilder:validation:Enum:=CatalogProvided;SelfCertified + // +kubebuilder:default:=CatalogProvided + // +optional UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"` } -// ServiceAccountReference references a serviceAccount. +// ServiceAccountReference identifies the serviceAccount used fo install a ClusterExtension. type ServiceAccountReference struct { // name is a required, immutable reference to the name of the ServiceAccount // to be used for installation and management of the content for the package // specified in the packageName field. // - // This ServiceAccount is expected to exist in the installNamespace. + // This ServiceAccount must exist in the installNamespace. // - // This field follows the DNS subdomain name standard as defined in [RFC - // 1123]. This means that valid values: - // - Contain no more than 253 characters - // - Contain only lowercase alphanumeric characters, '-', or '.' - // - Start with an alphanumeric character - // - End with an alphanumeric character + // name follows the DNS subdomain standard as defined in [RFC 1123]. + // It must contain only lowercase alphanumeric characters, + // hyphens (-) or periods (.), start and end with an alphanumeric character, + // and be no longer than 253 characters. // // Some examples of valid values are: // - some-serviceaccount @@ -370,13 +361,15 @@ type ServiceAccountReference struct { // // [RFC 1123]: https://tools.ietf.org/html/rfc1123 // - //+kubebuilder:validation:MaxLength:=253 - //+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - //+kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable" + // +kubebuilder:validation:MaxLength:=253 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="name must be a valid DNS1123 subdomain. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters" + // +kubebuilder:validation:Required Name string `json:"name"` } // PreflightConfig holds the configuration for the preflight checks. If used, at least one preflight check must be non-nil. +// // +kubebuilder:validation:XValidation:rule="has(self.crdUpgradeSafety)",message="at least one of [crdUpgradeSafety] are required when preflight is specified" type PreflightConfig struct { // crdUpgradeSafety is used to configure the CRD Upgrade Safety pre-flight @@ -384,34 +377,28 @@ type PreflightConfig struct { // // The CRD Upgrade Safety pre-flight check safeguards from unintended // consequences of upgrading a CRD, such as data loss. - // - // This field is required if the spec.install.preflight field is specified. CRDUpgradeSafety *CRDUpgradeSafetyPreflightConfig `json:"crdUpgradeSafety"` } // CRDUpgradeSafetyPreflightConfig is the configuration for CRD upgrade safety preflight check. type CRDUpgradeSafetyPreflightConfig struct { - // policy is used to configure the state of the CRD Upgrade Safety pre-flight check. - // - // This field is required when the spec.install.preflight.crdUpgradeSafety field is - // specified. + // enforcement is a required field, used to configure the state of the CRD Upgrade Safety pre-flight check. // - // Allowed values are ["Enabled", "Disabled"]. The default value is "Enabled". + // Allowed values are "None" or "Strict". The default value is "Strict". // - // When set to "Disabled", the CRD Upgrade Safety pre-flight check will be skipped + // When set to "None", the CRD Upgrade Safety pre-flight check will be skipped // when performing an upgrade operation. This should be used with caution as // unintended consequences such as data loss can occur. // - // When set to "Enabled", the CRD Upgrade Safety pre-flight check will be run when + // When set to "Strict", the CRD Upgrade Safety pre-flight check will be run when // performing an upgrade operation. // - //+kubebuilder:validation:Enum:="Enabled";"Disabled" - //+kubebuilder:default:=Enabled - Policy CRDUpgradeSafetyPolicy `json:"policy"` + // +kubebuilder:validation:Enum:="None";"Strict" + // +kubebuilder:validation:Required + Enforcement CRDUpgradeSafetyEnforcement `json:"enforcement"` } const ( - // TODO(user): add more Types, here and into init() TypeInstalled = "Installed" TypeProgressing = "Progressing" @@ -428,110 +415,111 @@ const ( ReasonBlocked = "Blocked" ReasonRetrying = "Retrying" - CRDUpgradeSafetyPolicyEnabled CRDUpgradeSafetyPolicy = "Enabled" - CRDUpgradeSafetyPolicyDisabled CRDUpgradeSafetyPolicy = "Disabled" + // None will not perform CRD upgrade safety checks. + CRDUpgradeSafetyEnforcementNone CRDUpgradeSafetyEnforcement = "None" + // Strict will enforce the CRD upgrade safety check and block the upgrade if the CRD would not pass the check. + CRDUpgradeSafetyEnforcementStrict CRDUpgradeSafetyEnforcement = "Strict" ) -func init() { - // TODO(user): add Types from above - conditionsets.ConditionTypes = append(conditionsets.ConditionTypes, - TypeInstalled, - TypeDeprecated, - TypePackageDeprecated, - TypeChannelDeprecated, - TypeBundleDeprecated, - TypeProgressing, - ) - // TODO(user): add Reasons from above - conditionsets.ConditionReasons = append(conditionsets.ConditionReasons, - ReasonSucceeded, - ReasonDeprecated, - ReasonFailed, - ReasonBlocked, - ReasonRetrying, - ) -} - +// BundleMetadata is a representation of the identifying attributes of a bundle. type BundleMetadata struct { - // name is a required field and is a reference - // to the name of a bundle + // name is required and follows the DNS subdomain standard + // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + // hyphens (-) or periods (.), start and end with an alphanumeric character, + // and be no longer than 253 characters. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="packageName must be a valid DNS1123 subdomain. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters" Name string `json:"name"` - // version is a required field and is a reference - // to the version that this bundle represents + + // version is a required field and is a reference to the version that this bundle represents + // version follows the semantic versioning standard as defined in https://semver.org/. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:XValidation:rule="self.matches(\"^([0-9]+)(\\\\.[0-9]+)?(\\\\.[0-9]+)?(-([-0-9A-Za-z]+(\\\\.[-0-9A-Za-z]+)*))?(\\\\+([-0-9A-Za-z]+(-\\\\.[-0-9A-Za-z]+)*))?\")",message="version must be well-formed semver" Version string `json:"version"` } -// ClusterExtensionStatus defines the observed state of ClusterExtension. +// ClusterExtensionStatus defines the observed state of a ClusterExtension. type ClusterExtensionStatus struct { - Install *ClusterExtensionInstallStatus `json:"install,omitempty"` - - // conditions is a representation of the current state for this ClusterExtension. - // The status is represented by a set of "conditions". - // - // Each condition is generally structured in the following format: - // - Type: a string representation of the condition type. More or less the condition "name". - // - Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"]. - // - Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations. - // - Message: a human readable message that further elaborates on the state of the condition + // The set of condition types which apply to all spec.source variations are Installed and Progressing. // - // The global set of condition types are: - // - "Installed", represents whether or not the a bundle has been installed for this ClusterExtension - // - "Progressing", represents whether or not the ClusterExtension is progressing towards a new state + // The Installed condition represents whether or not the bundle has been installed for this ClusterExtension. + // When Installed is True and the Reason is Succeeded, the bundle has been successfully installed. + // When Installed is False and the Reason is Failed, the bundle has failed to install. // - // When the ClusterExtension is sourced from a catalog, the following conditions are also possible: - // - "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types - // - "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated - // - "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated - // - "BundleDeprecated", represents whether or not the installed bundle is deprecated - // - // The current set of reasons are: - // - "Succeeded", this reason is set on the "Installed" and "Progressing" conditions when initial installation and progressing to a new state is successful - // - "Failed", this reason is set on the "Installed" condition when an error has occurred while performing the initial installation. - // - "Blocked", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that requires manual intervention for recovery - // - "Retrying", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that could be resolved on subsequent reconciliation attempts - // - "Deprecated", this reason is set on the "Deprecated", "PackageDeprecated", "ChannelDeprecated", and "BundleDeprecated" conditions to signal that the installed package has been deprecated at the particular scope + // The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. + // When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. + // When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. + // When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. // + // When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition. + // These are indications from a package owner to guide users away from a particular package, channel, or bundle. + // BundleDeprecated is set if the requested bundle version is marked deprecated in the catalog. + // ChannelDeprecated is set if the requested channel is marked deprecated in the catalog. + // PackageDeprecated is set if the requested package is marked deprecated in the catalog. + // Deprecated is a rollup condition that is present when any of the deprecated conditions are present. // // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type + // +optional Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + + // install is a representation of the current installation status for this ClusterExtension. + // + // +optional + Install *ClusterExtensionInstallStatus `json:"install,omitempty"` } +// ClusterExtensionInstallStatus is a representation of the status of the identified bundle. type ClusterExtensionInstallStatus struct { - // bundle is a representation of the currently installed bundle. + // bundle is a required field which represents the identifying attributes of a bundle. // // A "bundle" is a versioned set of content that represents the resources that // need to be applied to a cluster to install a package. + // + // +kubebuilder:validation:Required Bundle BundleMetadata `json:"bundle"` } -//+kubebuilder:object:root=true -//+kubebuilder:resource:scope=Cluster -//+kubebuilder:subresource:status -//+kubebuilder:printcolumn:name="Installed Bundle",type=string,JSONPath=`.status.install.bundle.name` -//+kubebuilder:printcolumn:name=Version,type=string,JSONPath=`.status.install.bundle.version` -//+kubebuilder:printcolumn:name="Installed",type=string,JSONPath=`.status.conditions[?(@.type=='Installed')].status` -//+kubebuilder:printcolumn:name="Progressing",type=string,JSONPath=`.status.conditions[?(@.type=='Progressing')].status` -//+kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp` +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Installed Bundle",type=string,JSONPath=`.status.install.bundle.name` +// +kubebuilder:printcolumn:name=Version,type=string,JSONPath=`.status.install.bundle.version` +// +kubebuilder:printcolumn:name="Installed",type=string,JSONPath=`.status.conditions[?(@.type=='Installed')].status` +// +kubebuilder:printcolumn:name="Progressing",type=string,JSONPath=`.status.conditions[?(@.type=='Progressing')].status` +// +kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp` // ClusterExtension is the Schema for the clusterextensions API type ClusterExtension struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec ClusterExtensionSpec `json:"spec,omitempty"` + // spec is an optional field that defines the desired state of the ClusterExtension. + // +optional + Spec ClusterExtensionSpec `json:"spec,omitempty"` + + // status is an optional field that defines the observed state of the ClusterExtension. + // +optional Status ClusterExtensionStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // ClusterExtensionList contains a list of ClusterExtension type ClusterExtensionList struct { metav1.TypeMeta `json:",inline"` + + // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []ClusterExtension `json:"items"` + + // items is a required list of ClusterExtension objects. + // + // +kubebuilder:validation:Required + Items []ClusterExtension `json:"items"` } func init() { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ccd143aec5..7ca0e77848 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -63,7 +63,11 @@ func (in *CatalogSource) DeepCopyInto(out *CatalogSource) { *out = make([]string, len(*in)) copy(*out, *in) } - in.Selector.DeepCopyInto(&out.Selector) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CatalogSource. @@ -192,11 +196,6 @@ func (in *ClusterExtensionSpec) DeepCopy() *ClusterExtensionSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) { *out = *in - if in.Install != nil { - in, out := &in.Install, &out.Install - *out = new(ClusterExtensionInstallStatus) - **out = **in - } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]v1.Condition, len(*in)) @@ -204,6 +203,11 @@ func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Install != nil { + in, out := &in.Install, &out.Install + *out = new(ClusterExtensionInstallStatus) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionStatus. diff --git a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml index 2550101474..106605424c 100644 --- a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml +++ b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -53,7 +53,8 @@ spec: metadata: type: object spec: - description: ClusterExtensionSpec defines the desired state of ClusterExtension + description: spec is an optional field that defines the desired state + of the ClusterExtension. properties: install: description: |- @@ -69,44 +70,34 @@ spec: properties: namespace: description: |- - namespace is a reference to the Namespace in which the bundle of - content for the package referenced in the packageName field will be applied. + namespace designates the kubernetes Namespace where bundle content + for the package, referenced in the 'packageName' field, will be applied and the necessary + service account can be found. The bundle may contain cluster-scoped resources or resources that are applied to other Namespaces. This Namespace is expected to exist. namespace is required, immutable, and follows the DNS label standard - as defined in [RFC 1123]. This means that valid values: - - Contain no more than 63 characters - - Contain only lowercase alphanumeric characters or '-' - - Start with an alphanumeric character - - End with an alphanumeric character - - Some examples of valid values are: - - some-namespace - - 123-namespace - - 1-namespace-2 - - somenamespace - - Some examples of invalid values are: - - -some-namespace - - some-namespace- - - thisisareallylongnamespacenamethatisgreaterthanthemaximumlength - - some.namespace + as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-), + start and end with an alphanumeric character, and be no longer than 63 characters [RFC 1123]: https://tools.ietf.org/html/rfc1123 maxLength: 63 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string x-kubernetes-validations: - message: namespace is immutable rule: self == oldSelf + - message: namespace must be a valid DNS1123 label. It must contain + only lowercase alphanumeric characters or hyphens (-), start + and end with an alphanumeric character, and be no longer than + 63 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") preflight: description: |- - preflight is an optional field that can be used to configure the preflight checks run before installation or upgrade of the content for the package specified in the packageName field. + preflight is an optional field that can be used to configure the checks that are + run before installation or upgrade of the content for the package specified in the packageName field. - When specified, it overrides the default configuration of the preflight checks that are required to execute successfully during an install/upgrade operation. - - When not specified, the default configuration for each preflight check will be used. + When specified, it replaces the default preflight configuration for install/upgrade actions. + When not specified, the default configuration will be used. properties: crdUpgradeSafety: description: |- @@ -115,31 +106,25 @@ spec: The CRD Upgrade Safety pre-flight check safeguards from unintended consequences of upgrading a CRD, such as data loss. - - This field is required if the spec.install.preflight field is specified. properties: - policy: - default: Enabled + enforcement: description: |- - policy is used to configure the state of the CRD Upgrade Safety pre-flight check. + enforcement is a required field, used to configure the state of the CRD Upgrade Safety pre-flight check. - This field is required when the spec.install.preflight.crdUpgradeSafety field is - specified. + Allowed values are "None" or "Strict". The default value is "Strict". - Allowed values are ["Enabled", "Disabled"]. The default value is "Enabled". - - When set to "Disabled", the CRD Upgrade Safety pre-flight check will be skipped + When set to "None", the CRD Upgrade Safety pre-flight check will be skipped when performing an upgrade operation. This should be used with caution as unintended consequences such as data loss can occur. - When set to "Enabled", the CRD Upgrade Safety pre-flight check will be run when + When set to "Strict", the CRD Upgrade Safety pre-flight check will be run when performing an upgrade operation. enum: - - Enabled - - Disabled + - None + - Strict type: string required: - - policy + - enforcement type: object required: - crdUpgradeSafety @@ -151,7 +136,7 @@ spec: serviceAccount: description: |- serviceAccount is a required reference to a ServiceAccount that exists - in the installNamespace. The provided ServiceAccount is used to install and + in the installNamespace which is used to install and manage the content for the package specified in the packageName field. In order to successfully install and manage the content for the package, @@ -165,14 +150,12 @@ spec: to be used for installation and management of the content for the package specified in the packageName field. - This ServiceAccount is expected to exist in the installNamespace. + This ServiceAccount must exist in the installNamespace. - This field follows the DNS subdomain name standard as defined in [RFC - 1123]. This means that valid values: - - Contain no more than 253 characters - - Contain only lowercase alphanumeric characters, '-', or '.' - - Start with an alphanumeric character - - End with an alphanumeric character + name follows the DNS subdomain standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric character, + and be no longer than 253 characters. Some examples of valid values are: - some-serviceaccount @@ -187,11 +170,15 @@ spec: [RFC 1123]: https://tools.ietf.org/html/rfc1123 maxLength: 253 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string x-kubernetes-validations: - message: name is immutable rule: self == oldSelf + - message: name must be a valid DNS1123 subdomain. It must + contain only lowercase alphanumeric characters, hyphens + (-) or periods (.), start and end with an alphanumeric + character, and be no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") required: - name type: object @@ -216,15 +203,20 @@ spec: properties: catalog: description: |- - catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog", - and must be the only field defined for this sourceType. + catalog is used to configure how information is sourced from a catalog. + This field is required when sourceType is "Catalog", and forbidden otherwise. properties: channels: description: |- channels is an optional reference to a set of channels belonging to the package specified in the packageName field. - A "channel" is a package author defined stream of updates for an extension. + A "channel" is a package-author-defined stream of updates for an extension. + + Each channel in the list must follow the DNS subdomain standard + as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric character, + and be no longer than 253 characters. No more than 256 channels can be specified. When specified, it is used to constrain the set of installable bundles and the automated upgrade path. This constraint is an AND operation with the @@ -236,13 +228,6 @@ spec: When unspecified, upgrade edges across all channels will be used to identify valid automatic upgrade paths. - This field follows the DNS subdomain name standard as defined in [RFC - 1123]. This means that valid entries: - - Contain no more than 253 characters - - Contain only lowercase alphanumeric characters, '-', or '.' - - Start with an alphanumeric character - - End with an alphanumeric character - Some examples of valid values are: - 1.1.x - alpha @@ -263,20 +248,21 @@ spec: [RFC 1123]: https://tools.ietf.org/html/rfc1123 items: maxLength: 253 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string + x-kubernetes-validations: + - message: channels entries must be valid DNS1123 subdomains + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + maxItems: 256 type: array packageName: description: |- packageName is a reference to the name of the package to be installed and is used to filter the content from catalogs. - This field is required, immutable and follows the DNS subdomain name - standard as defined in [RFC 1123]. This means that valid entries: - - Contain no more than 253 characters - - Contain only lowercase alphanumeric characters, '-', or '.' - - Start with an alphanumeric character - - End with an alphanumeric character + packageName is required, immutable, and follows the DNS subdomain standard + as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric character, + and be no longer than 253 characters. Some examples of valid values are: - some-package @@ -292,11 +278,15 @@ spec: [RFC 1123]: https://tools.ietf.org/html/rfc1123 maxLength: 253 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string x-kubernetes-validations: - message: packageName is immutable rule: self == oldSelf + - message: packageName must be a valid DNS1123 subdomain. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric + character, and be no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") selector: description: |- selector is an optional field that can be used @@ -356,7 +346,7 @@ spec: the upgrade path(s) defined in the catalog are enforced for the package referenced in the packageName field. - Allowed values are: ["CatalogProvided", "SelfCertified"]. + Allowed values are: "CatalogProvided" or "SelfCertified", or omitted. When this field is set to "CatalogProvided", automatic upgrades will only occur when upgrade constraints specified by the package author are met. @@ -368,7 +358,7 @@ spec: loss. It is assumed that users have independently verified changes when using this option. - If unspecified, the default value is "CatalogProvided". + When this field is omitted, the default value is "CatalogProvided". enum: - CatalogProvided - SelfCertified @@ -449,8 +439,10 @@ spec: For more information on semver, please see https://semver.org/ maxLength: 64 - pattern: ^(\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\^)\s*(v?(0|[1-9]\d*|[x|X|\*])(\.(0|[1-9]\d*|x|X|\*]))?(\.(0|[1-9]\d*|x|X|\*))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)((?:\s+|,\s*|\s*\|\|\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\^)\s*(v?(0|[1-9]\d*|x|X|\*])(\.(0|[1-9]\d*|x|X|\*))?(\.(0|[1-9]\d*|x|X|\*]))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)*$ type: string + x-kubernetes-validations: + - message: invalid version expression + rule: self.matches("^(\\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|[x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*]))?(\\.(0|[1-9]\\d*|x|X|\\*))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)((?:\\s+|,\\s*|\\s*\\|\\|\\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*))?(\\.(0|[1-9]\\d*|x|X|\\*]))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)*$") required: - packageName type: object @@ -458,11 +450,12 @@ spec: description: |- sourceType is a required reference to the type of install source. - Allowed values are ["Catalog"] + Allowed values are "Catalog" - When this field is set to "Catalog", information for determining the appropriate - bundle of content to install will be fetched from ClusterCatalog resources existing - on the cluster. When using the Catalog sourceType, the catalog field must also be set. + When this field is set to "Catalog", information for determining the + appropriate bundle of content to install will be fetched from + ClusterCatalog resources existing on the cluster. + When using the Catalog sourceType, the catalog field must also be set. enum: - Catalog type: string @@ -470,42 +463,37 @@ spec: - sourceType type: object x-kubernetes-validations: - - message: sourceType Catalog requires catalog field - rule: self.sourceType == 'Catalog' && has(self.catalog) + - message: catalog is required when sourceType is Catalog, and forbidden + otherwise + rule: 'has(self.sourceType) && self.sourceType == ''Catalog'' ? + has(self.catalog) : !has(self.catalog)' required: - install - source type: object status: - description: ClusterExtensionStatus defines the observed state of ClusterExtension. + description: status is an optional field that defines the observed state + of the ClusterExtension. properties: conditions: description: |- - conditions is a representation of the current state for this ClusterExtension. - The status is represented by a set of "conditions". - - Each condition is generally structured in the following format: - - Type: a string representation of the condition type. More or less the condition "name". - - Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"]. - - Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations. - - Message: a human readable message that further elaborates on the state of the condition - - The global set of condition types are: - - "Installed", represents whether or not the a bundle has been installed for this ClusterExtension - - "Progressing", represents whether or not the ClusterExtension is progressing towards a new state - - When the ClusterExtension is sourced from a catalog, the following conditions are also possible: - - "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types - - "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated - - "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated - - "BundleDeprecated", represents whether or not the installed bundle is deprecated - - The current set of reasons are: - - "Succeeded", this reason is set on the "Installed" and "Progressing" conditions when initial installation and progressing to a new state is successful - - "Failed", this reason is set on the "Installed" condition when an error has occurred while performing the initial installation. - - "Blocked", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that requires manual intervention for recovery - - "Retrying", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that could be resolved on subsequent reconciliation attempts - - "Deprecated", this reason is set on the "Deprecated", "PackageDeprecated", "ChannelDeprecated", and "BundleDeprecated" conditions to signal that the installed package has been deprecated at the particular scope + The set of condition types which apply to all spec.source variations are Installed and Progressing. + + The Installed condition represents whether or not the bundle has been installed for this ClusterExtension. + When Installed is True and the Reason is Succeeded, the bundle has been successfully installed. + When Installed is False and the Reason is Failed, the bundle has failed to install. + + The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. + When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. + When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. + When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. + + When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition. + These are indications from a package owner to guide users away from a particular package, channel, or bundle. + BundleDeprecated is set if the requested bundle version is marked deprecated in the catalog. + ChannelDeprecated is set if the requested channel is marked deprecated in the catalog. + PackageDeprecated is set if the requested package is marked deprecated in the catalog. + Deprecated is a rollup condition that is present when any of the deprecated conditions are present. items: description: Condition contains details for one aspect of the current state of this API Resource. @@ -565,24 +553,37 @@ spec: - type x-kubernetes-list-type: map install: + description: install is a representation of the current installation + status for this ClusterExtension. properties: bundle: description: |- - bundle is a representation of the currently installed bundle. + bundle is a required field which represents the identifying attributes of a bundle. A "bundle" is a versioned set of content that represents the resources that need to be applied to a cluster to install a package. properties: name: description: |- - name is a required field and is a reference - to the name of a bundle + name is required and follows the DNS subdomain standard + as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric character, + and be no longer than 253 characters. type: string + x-kubernetes-validations: + - message: packageName must be a valid DNS1123 subdomain. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric + character, and be no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") version: description: |- - version is a required field and is a reference - to the version that this bundle represents + version is a required field and is a reference to the version that this bundle represents + version follows the semantic versioning standard as defined in https://semver.org/. type: string + x-kubernetes-validations: + - message: version must be well-formed semver + rule: self.matches("^([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-([-0-9A-Za-z]+(\\.[-0-9A-Za-z]+)*))?(\\+([-0-9A-Za-z]+(-\\.[-0-9A-Za-z]+)*))?") required: - name - version diff --git a/docs/api-reference/operator-controller-api-reference.md b/docs/api-reference/operator-controller-api-reference.md index 86bd901902..f6553a7a44 100644 --- a/docs/api-reference/operator-controller-api-reference.md +++ b/docs/api-reference/operator-controller-api-reference.md @@ -18,7 +18,7 @@ Package v1alpha1 contains API Schema definitions for the olm v1alpha1 API group - +BundleMetadata is a representation of the identifying attributes of a bundle. @@ -27,11 +27,11 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `name` _string_ | name is a required field and is a reference
to the name of a bundle | | | -| `version` _string_ | version is a required field and is a reference
to the version that this bundle represents | | | +| `name` _string_ | name is required and follows the DNS subdomain standard
as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters,
hyphens (-) or periods (.), start and end with an alphanumeric character,
and be no longer than 253 characters. | | Required: \{\}
| +| `version` _string_ | version is a required field and is a reference to the version that this bundle represents
version follows the semantic versioning standard as defined in https://semver.org/. | | Required: \{\}
| -#### CRDUpgradeSafetyPolicy +#### CRDUpgradeSafetyEnforcement _Underlying type:_ _string_ @@ -44,8 +44,8 @@ _Appears in:_ | Field | Description | | --- | --- | -| `Enabled` | | -| `Disabled` | | +| `None` | None will not perform CRD upgrade safety checks.
| +| `Strict` | Strict will enforce the CRD upgrade safety check and block the upgrade if the CRD would not pass the check.
| #### CRDUpgradeSafetyPreflightConfig @@ -61,14 +61,14 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `policy` _[CRDUpgradeSafetyPolicy](#crdupgradesafetypolicy)_ | policy is used to configure the state of the CRD Upgrade Safety pre-flight check.

This field is required when the spec.install.preflight.crdUpgradeSafety field is
specified.

Allowed values are ["Enabled", "Disabled"]. The default value is "Enabled".

When set to "Disabled", the CRD Upgrade Safety pre-flight check will be skipped
when performing an upgrade operation. This should be used with caution as
unintended consequences such as data loss can occur.

When set to "Enabled", the CRD Upgrade Safety pre-flight check will be run when
performing an upgrade operation. | Enabled | Enum: [Enabled Disabled]
| +| `enforcement` _[CRDUpgradeSafetyEnforcement](#crdupgradesafetyenforcement)_ | enforcement is a required field, used to configure the state of the CRD Upgrade Safety pre-flight check.

Allowed values are "None" or "Strict". The default value is "Strict".

When set to "None", the CRD Upgrade Safety pre-flight check will be skipped
when performing an upgrade operation. This should be used with caution as
unintended consequences such as data loss can occur.

When set to "Strict", the CRD Upgrade Safety pre-flight check will be run when
performing an upgrade operation. | | Enum: [None Strict]
Required: \{\}
| #### CatalogSource -CatalogSource defines the required fields for catalog source. +CatalogSource defines the attributes used to identify and filter content from a catalog. @@ -77,11 +77,11 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `packageName` _string_ | packageName is a reference to the name of the package to be installed
and is used to filter the content from catalogs.

This field is required, immutable and follows the DNS subdomain name
standard as defined in [RFC 1123]. This means that valid entries:
- Contain no more than 253 characters
- Contain only lowercase alphanumeric characters, '-', or '.'
- Start with an alphanumeric character
- End with an alphanumeric character

Some examples of valid values are:
- some-package
- 123-package
- 1-package-2
- somepackage

Some examples of invalid values are:
- -some-package
- some-package-
- thisisareallylongpackagenamethatisgreaterthanthemaximumlength
- some.package

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 253
Pattern: `^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
| -| `version` _string_ | version is an optional semver constraint (a specific version or range of versions). When unspecified, the latest version available will be installed.

Acceptable version ranges are no longer than 64 characters.
Version ranges are composed of comma- or space-delimited values and one or
more comparison operators, known as comparison strings. Additional
comparison strings can be added using the OR operator (\|\|).

# Range Comparisons

To specify a version range, you can use a comparison string like ">=3.0,
<3.6". When specifying a range, automatic updates will occur within that
range. The example comparison string means "install any version greater than
or equal to 3.0.0 but less than 3.6.0.". It also states intent that if any
upgrades are available within the version range after initial installation,
those upgrades should be automatically performed.

# Pinned Versions

To specify an exact version to install you can use a version range that
"pins" to a specific version. When pinning to a specific version, no
automatic updates will occur. An example of a pinned version range is
"0.6.0", which means "only install version 0.6.0 and never
upgrade from this version".

# Basic Comparison Operators

The basic comparison operators and their meanings are:
- "=", equal (not aliased to an operator)
- "!=", not equal
- "<", less than
- ">", greater than
- ">=", greater than OR equal to
- "<=", less than OR equal to

# Wildcard Comparisons

You can use the "x", "X", and "*" characters as wildcard characters in all
comparison operations. Some examples of using the wildcard characters:
- "1.2.x", "1.2.X", and "1.2.*" is equivalent to ">=1.2.0, < 1.3.0"
- ">= 1.2.x", ">= 1.2.X", and ">= 1.2.*" is equivalent to ">= 1.2.0"
- "<= 2.x", "<= 2.X", and "<= 2.*" is equivalent to "< 3"
- "x", "X", and "*" is equivalent to ">= 0.0.0"

# Patch Release Comparisons

When you want to specify a minor version up to the next major version you
can use the "~" character to perform patch comparisons. Some examples:
- "~1.2.3" is equivalent to ">=1.2.3, <1.3.0"
- "~1" and "~1.x" is equivalent to ">=1, <2"
- "~2.3" is equivalent to ">=2.3, <2.4"
- "~1.2.x" is equivalent to ">=1.2.0, <1.3.0"

# Major Release Comparisons

You can use the "^" character to make major release comparisons after a
stable 1.0.0 version is published. If there is no stable version published, // minor versions define the stability level. Some examples:
- "^1.2.3" is equivalent to ">=1.2.3, <2.0.0"
- "^1.2.x" is equivalent to ">=1.2.0, <2.0.0"
- "^2.3" is equivalent to ">=2.3, <3"
- "^2.x" is equivalent to ">=2.0.0, <3"
- "^0.2.3" is equivalent to ">=0.2.3, <0.3.0"
- "^0.2" is equivalent to ">=0.2.0, <0.3.0"
- "^0.0.3" is equvalent to ">=0.0.3, <0.0.4"
- "^0.0" is equivalent to ">=0.0.0, <0.1.0"
- "^0" is equivalent to ">=0.0.0, <1.0.0"

# OR Comparisons
You can use the "\|\|" character to represent an OR operation in the version
range. Some examples:
- ">=1.2.3, <2.0.0 \|\| >3.0.0"
- "^0 \|\| ^3 \|\| ^5"

For more information on semver, please see https://semver.org/ | | MaxLength: 64
Pattern: `^(\s*(=\|\|!=\|>\|<\|>=\|=>\|<=\|=<\|~\|~>\|\^)\s*(v?(0\|[1-9]\d*\|[x\|X\|\*])(\.(0\|[1-9]\d*\|x\|X\|\*]))?(\.(0\|[1-9]\d*\|x\|X\|\*))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)((?:\s+\|,\s*\|\s*\\|\\|\s*)(=\|\|!=\|>\|<\|>=\|=>\|<=\|=<\|~\|~>\|\^)\s*(v?(0\|[1-9]\d*\|x\|X\|\*])(\.(0\|[1-9]\d*\|x\|X\|\*))?(\.(0\|[1-9]\d*\|x\|X\|\*]))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)*$`
| -| `channels` _string array_ | channels is an optional reference to a set of channels belonging to
the package specified in the packageName field.

A "channel" is a package author defined stream of updates for an extension.

When specified, it is used to constrain the set of installable bundles and
the automated upgrade path. This constraint is an AND operation with the
version field. For example:
- Given channel is set to "foo"
- Given version is set to ">=1.0.0, <1.5.0"
- Only bundles that exist in channel "foo" AND satisfy the version range comparison will be considered installable
- Automatic upgrades will be constrained to upgrade edges defined by the selected channel

When unspecified, upgrade edges across all channels will be used to identify valid automatic upgrade paths.

This field follows the DNS subdomain name standard as defined in [RFC
1123]. This means that valid entries:
- Contain no more than 253 characters
- Contain only lowercase alphanumeric characters, '-', or '.'
- Start with an alphanumeric character
- End with an alphanumeric character

Some examples of valid values are:
- 1.1.x
- alpha
- stable
- stable-v1
- v1-stable
- dev-preview
- preview
- community

Some examples of invalid values are:
- -some-channel
- some-channel-
- thisisareallylongchannelnamethatisgreaterthanthemaximumlength
- original_40
- --default-channel

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | | +| `packageName` _string_ | packageName is a reference to the name of the package to be installed
and is used to filter the content from catalogs.

packageName is required, immutable, and follows the DNS subdomain standard
as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters,
hyphens (-) or periods (.), start and end with an alphanumeric character,
and be no longer than 253 characters.

Some examples of valid values are:
- some-package
- 123-package
- 1-package-2
- somepackage

Some examples of invalid values are:
- -some-package
- some-package-
- thisisareallylongpackagenamethatisgreaterthanthemaximumlength
- some.package

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 253
Required: \{\}
| +| `version` _string_ | version is an optional semver constraint (a specific version or range of versions). When unspecified, the latest version available will be installed.

Acceptable version ranges are no longer than 64 characters.
Version ranges are composed of comma- or space-delimited values and one or
more comparison operators, known as comparison strings. Additional
comparison strings can be added using the OR operator (\|\|).

# Range Comparisons

To specify a version range, you can use a comparison string like ">=3.0,
<3.6". When specifying a range, automatic updates will occur within that
range. The example comparison string means "install any version greater than
or equal to 3.0.0 but less than 3.6.0.". It also states intent that if any
upgrades are available within the version range after initial installation,
those upgrades should be automatically performed.

# Pinned Versions

To specify an exact version to install you can use a version range that
"pins" to a specific version. When pinning to a specific version, no
automatic updates will occur. An example of a pinned version range is
"0.6.0", which means "only install version 0.6.0 and never
upgrade from this version".

# Basic Comparison Operators

The basic comparison operators and their meanings are:
- "=", equal (not aliased to an operator)
- "!=", not equal
- "<", less than
- ">", greater than
- ">=", greater than OR equal to
- "<=", less than OR equal to

# Wildcard Comparisons

You can use the "x", "X", and "*" characters as wildcard characters in all
comparison operations. Some examples of using the wildcard characters:
- "1.2.x", "1.2.X", and "1.2.*" is equivalent to ">=1.2.0, < 1.3.0"
- ">= 1.2.x", ">= 1.2.X", and ">= 1.2.*" is equivalent to ">= 1.2.0"
- "<= 2.x", "<= 2.X", and "<= 2.*" is equivalent to "< 3"
- "x", "X", and "*" is equivalent to ">= 0.0.0"

# Patch Release Comparisons

When you want to specify a minor version up to the next major version you
can use the "~" character to perform patch comparisons. Some examples:
- "~1.2.3" is equivalent to ">=1.2.3, <1.3.0"
- "~1" and "~1.x" is equivalent to ">=1, <2"
- "~2.3" is equivalent to ">=2.3, <2.4"
- "~1.2.x" is equivalent to ">=1.2.0, <1.3.0"

# Major Release Comparisons

You can use the "^" character to make major release comparisons after a
stable 1.0.0 version is published. If there is no stable version published, // minor versions define the stability level. Some examples:
- "^1.2.3" is equivalent to ">=1.2.3, <2.0.0"
- "^1.2.x" is equivalent to ">=1.2.0, <2.0.0"
- "^2.3" is equivalent to ">=2.3, <3"
- "^2.x" is equivalent to ">=2.0.0, <3"
- "^0.2.3" is equivalent to ">=0.2.3, <0.3.0"
- "^0.2" is equivalent to ">=0.2.0, <0.3.0"
- "^0.0.3" is equvalent to ">=0.0.3, <0.0.4"
- "^0.0" is equivalent to ">=0.0.0, <0.1.0"
- "^0" is equivalent to ">=0.0.0, <1.0.0"

# OR Comparisons
You can use the "\|\|" character to represent an OR operation in the version
range. Some examples:
- ">=1.2.3, <2.0.0 \|\| >3.0.0"
- "^0 \|\| ^3 \|\| ^5"

For more information on semver, please see https://semver.org/ | | MaxLength: 64
| +| `channels` _string array_ | channels is an optional reference to a set of channels belonging to
the package specified in the packageName field.

A "channel" is a package-author-defined stream of updates for an extension.

Each channel in the list must follow the DNS subdomain standard
as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters,
hyphens (-) or periods (.), start and end with an alphanumeric character,
and be no longer than 253 characters. No more than 256 channels can be specified.

When specified, it is used to constrain the set of installable bundles and
the automated upgrade path. This constraint is an AND operation with the
version field. For example:
- Given channel is set to "foo"
- Given version is set to ">=1.0.0, <1.5.0"
- Only bundles that exist in channel "foo" AND satisfy the version range comparison will be considered installable
- Automatic upgrades will be constrained to upgrade edges defined by the selected channel

When unspecified, upgrade edges across all channels will be used to identify valid automatic upgrade paths.

Some examples of valid values are:
- 1.1.x
- alpha
- stable
- stable-v1
- v1-stable
- dev-preview
- preview
- community

Some examples of invalid values are:
- -some-channel
- some-channel-
- thisisareallylongchannelnamethatisgreaterthanthemaximumlength
- original_40
- --default-channel

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxItems: 256
| | `selector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#labelselector-v1-meta)_ | selector is an optional field that can be used
to filter the set of ClusterCatalogs used in the bundle
selection process.

When unspecified, all ClusterCatalogs will be used in
the bundle selection process. | | | -| `upgradeConstraintPolicy` _[UpgradeConstraintPolicy](#upgradeconstraintpolicy)_ | upgradeConstraintPolicy is an optional field that controls whether
the upgrade path(s) defined in the catalog are enforced for the package
referenced in the packageName field.

Allowed values are: ["CatalogProvided", "SelfCertified"].

When this field is set to "CatalogProvided", automatic upgrades will only occur
when upgrade constraints specified by the package author are met.

When this field is set to "SelfCertified", the upgrade constraints specified by
the package author are ignored. This allows for upgrades and downgrades to
any version of the package. This is considered a dangerous operation as it
can lead to unknown and potentially disastrous outcomes, such as data
loss. It is assumed that users have independently verified changes when
using this option.

If unspecified, the default value is "CatalogProvided". | CatalogProvided | Enum: [CatalogProvided SelfCertified]
| +| `upgradeConstraintPolicy` _[UpgradeConstraintPolicy](#upgradeconstraintpolicy)_ | upgradeConstraintPolicy is an optional field that controls whether
the upgrade path(s) defined in the catalog are enforced for the package
referenced in the packageName field.

Allowed values are: "CatalogProvided" or "SelfCertified", or omitted.

When this field is set to "CatalogProvided", automatic upgrades will only occur
when upgrade constraints specified by the package author are met.

When this field is set to "SelfCertified", the upgrade constraints specified by
the package author are ignored. This allows for upgrades and downgrades to
any version of the package. This is considered a dangerous operation as it
can lead to unknown and potentially disastrous outcomes, such as data
loss. It is assumed that users have independently verified changes when
using this option.

When this field is omitted, the default value is "CatalogProvided". | CatalogProvided | Enum: [CatalogProvided SelfCertified]
| #### ClusterExtension @@ -102,8 +102,8 @@ _Appears in:_ | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `spec` _[ClusterExtensionSpec](#clusterextensionspec)_ | | | | -| `status` _[ClusterExtensionStatus](#clusterextensionstatus)_ | | | | +| `spec` _[ClusterExtensionSpec](#clusterextensionspec)_ | spec is an optional field that defines the desired state of the ClusterExtension. | | | +| `status` _[ClusterExtensionStatus](#clusterextensionstatus)_ | status is an optional field that defines the observed state of the ClusterExtension. | | | #### ClusterExtensionInstallConfig @@ -120,16 +120,16 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `namespace` _string_ | namespace is a reference to the Namespace in which the bundle of
content for the package referenced in the packageName field will be applied.
The bundle may contain cluster-scoped resources or resources that are
applied to other Namespaces. This Namespace is expected to exist.

namespace is required, immutable, and follows the DNS label standard
as defined in [RFC 1123]. This means that valid values:
- Contain no more than 63 characters
- Contain only lowercase alphanumeric characters or '-'
- Start with an alphanumeric character
- End with an alphanumeric character

Some examples of valid values are:
- some-namespace
- 123-namespace
- 1-namespace-2
- somenamespace

Some examples of invalid values are:
- -some-namespace
- some-namespace-
- thisisareallylongnamespacenamethatisgreaterthanthemaximumlength
- some.namespace

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 63
Pattern: `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
| -| `serviceAccount` _[ServiceAccountReference](#serviceaccountreference)_ | serviceAccount is a required reference to a ServiceAccount that exists
in the installNamespace. The provided ServiceAccount is used to install and
manage the content for the package specified in the packageName field.

In order to successfully install and manage the content for the package,
the ServiceAccount provided via this field should be configured with the
appropriate permissions to perform the necessary operations on all the
resources that are included in the bundle of content being applied. | | | -| `preflight` _[PreflightConfig](#preflightconfig)_ | preflight is an optional field that can be used to configure the preflight checks run before installation or upgrade of the content for the package specified in the packageName field.

When specified, it overrides the default configuration of the preflight checks that are required to execute successfully during an install/upgrade operation.

When not specified, the default configuration for each preflight check will be used. | | | +| `namespace` _string_ | namespace designates the kubernetes Namespace where bundle content
for the package, referenced in the 'packageName' field, will be applied and the necessary
service account can be found.
The bundle may contain cluster-scoped resources or resources that are
applied to other Namespaces. This Namespace is expected to exist.

namespace is required, immutable, and follows the DNS label standard
as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-),
start and end with an alphanumeric character, and be no longer than 63 characters

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 63
Required: \{\}
| +| `serviceAccount` _[ServiceAccountReference](#serviceaccountreference)_ | serviceAccount is a required reference to a ServiceAccount that exists
in the installNamespace which is used to install and
manage the content for the package specified in the packageName field.

In order to successfully install and manage the content for the package,
the ServiceAccount provided via this field should be configured with the
appropriate permissions to perform the necessary operations on all the
resources that are included in the bundle of content being applied. | | Required: \{\}
| +| `preflight` _[PreflightConfig](#preflightconfig)_ | preflight is an optional field that can be used to configure the checks that are
run before installation or upgrade of the content for the package specified in the packageName field.

When specified, it replaces the default preflight configuration for install/upgrade actions.
When not specified, the default configuration will be used. | | | #### ClusterExtensionInstallStatus - +ClusterExtensionInstallStatus is a representation of the status of the identified bundle. @@ -138,7 +138,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `bundle` _[BundleMetadata](#bundlemetadata)_ | bundle is a representation of the currently installed bundle.

A "bundle" is a versioned set of content that represents the resources that
need to be applied to a cluster to install a package. | | | +| `bundle` _[BundleMetadata](#bundlemetadata)_ | bundle is a required field which represents the identifying attributes of a bundle.

A "bundle" is a versioned set of content that represents the resources that
need to be applied to a cluster to install a package. | | Required: \{\}
| #### ClusterExtensionList @@ -158,7 +158,7 @@ ClusterExtensionList contains a list of ClusterExtension | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | | `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `items` _[ClusterExtension](#clusterextension) array_ | | | | +| `items` _[ClusterExtension](#clusterextension) array_ | items is a required list of ClusterExtension objects. | | Required: \{\}
| #### ClusterExtensionSpec @@ -174,15 +174,15 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `source` _[SourceConfig](#sourceconfig)_ | source is a required field which selects the installation source of content
for this ClusterExtension. Selection is performed by setting the sourceType.

Catalog is currently the only implemented sourceType, and setting the
sourcetype to "Catalog" requires the catalog field to also be defined.

Below is a minimal example of a source definition (in yaml):

source:
sourceType: Catalog
catalog:
packageName: example-package | | | -| `install` _[ClusterExtensionInstallConfig](#clusterextensioninstallconfig)_ | install is a required field used to configure the installation options
for the ClusterExtension such as the installation namespace,
the service account and the pre-flight check configuration.

Below is a minimal example of an installation definition (in yaml):
install:
namespace: example-namespace
serviceAccount:
name: example-sa | | | +| `source` _[SourceConfig](#sourceconfig)_ | source is a required field which selects the installation source of content
for this ClusterExtension. Selection is performed by setting the sourceType.

Catalog is currently the only implemented sourceType, and setting the
sourcetype to "Catalog" requires the catalog field to also be defined.

Below is a minimal example of a source definition (in yaml):

source:
sourceType: Catalog
catalog:
packageName: example-package | | Required: \{\}
| +| `install` _[ClusterExtensionInstallConfig](#clusterextensioninstallconfig)_ | install is a required field used to configure the installation options
for the ClusterExtension such as the installation namespace,
the service account and the pre-flight check configuration.

Below is a minimal example of an installation definition (in yaml):
install:
namespace: example-namespace
serviceAccount:
name: example-sa | | Required: \{\}
| #### ClusterExtensionStatus -ClusterExtensionStatus defines the observed state of ClusterExtension. +ClusterExtensionStatus defines the observed state of a ClusterExtension. @@ -191,8 +191,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `install` _[ClusterExtensionInstallStatus](#clusterextensioninstallstatus)_ | | | | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterExtension.
The status is represented by a set of "conditions".

Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human readable message that further elaborates on the state of the condition

The global set of condition types are:
- "Installed", represents whether or not the a bundle has been installed for this ClusterExtension
- "Progressing", represents whether or not the ClusterExtension is progressing towards a new state

When the ClusterExtension is sourced from a catalog, the following conditions are also possible:
- "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types
- "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated
- "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated
- "BundleDeprecated", represents whether or not the installed bundle is deprecated

The current set of reasons are:
- "Succeeded", this reason is set on the "Installed" and "Progressing" conditions when initial installation and progressing to a new state is successful
- "Failed", this reason is set on the "Installed" condition when an error has occurred while performing the initial installation.
- "Blocked", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that requires manual intervention for recovery
- "Retrying", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that could be resolved on subsequent reconciliation attempts
- "Deprecated", this reason is set on the "Deprecated", "PackageDeprecated", "ChannelDeprecated", and "BundleDeprecated" conditions to signal that the installed package has been deprecated at the particular scope | | | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | The set of condition types which apply to all spec.source variations are Installed and Progressing.

The Installed condition represents whether or not the bundle has been installed for this ClusterExtension.
When Installed is True and the Reason is Succeeded, the bundle has been successfully installed.
When Installed is False and the Reason is Failed, the bundle has failed to install.

The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state.
When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state.
When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts.
When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery.

When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition.
These are indications from a package owner to guide users away from a particular package, channel, or bundle.
BundleDeprecated is set if the requested bundle version is marked deprecated in the catalog.
ChannelDeprecated is set if the requested channel is marked deprecated in the catalog.
PackageDeprecated is set if the requested package is marked deprecated in the catalog.
Deprecated is a rollup condition that is present when any of the deprecated conditions are present. | | | +| `install` _[ClusterExtensionInstallStatus](#clusterextensioninstallstatus)_ | install is a representation of the current installation status for this ClusterExtension. | | | #### PreflightConfig @@ -208,14 +208,14 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `crdUpgradeSafety` _[CRDUpgradeSafetyPreflightConfig](#crdupgradesafetypreflightconfig)_ | crdUpgradeSafety is used to configure the CRD Upgrade Safety pre-flight
checks that run prior to upgrades of installed content.

The CRD Upgrade Safety pre-flight check safeguards from unintended
consequences of upgrading a CRD, such as data loss.

This field is required if the spec.install.preflight field is specified. | | | +| `crdUpgradeSafety` _[CRDUpgradeSafetyPreflightConfig](#crdupgradesafetypreflightconfig)_ | crdUpgradeSafety is used to configure the CRD Upgrade Safety pre-flight
checks that run prior to upgrades of installed content.

The CRD Upgrade Safety pre-flight check safeguards from unintended
consequences of upgrading a CRD, such as data loss. | | | #### ServiceAccountReference -ServiceAccountReference references a serviceAccount. +ServiceAccountReference identifies the serviceAccount used fo install a ClusterExtension. @@ -224,7 +224,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `name` _string_ | name is a required, immutable reference to the name of the ServiceAccount
to be used for installation and management of the content for the package
specified in the packageName field.

This ServiceAccount is expected to exist in the installNamespace.

This field follows the DNS subdomain name standard as defined in [RFC
1123]. This means that valid values:
- Contain no more than 253 characters
- Contain only lowercase alphanumeric characters, '-', or '.'
- Start with an alphanumeric character
- End with an alphanumeric character

Some examples of valid values are:
- some-serviceaccount
- 123-serviceaccount
- 1-serviceaccount-2
- someserviceaccount
- some.serviceaccount

Some examples of invalid values are:
- -some-serviceaccount
- some-serviceaccount-

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 253
Pattern: `^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
| +| `name` _string_ | name is a required, immutable reference to the name of the ServiceAccount
to be used for installation and management of the content for the package
specified in the packageName field.

This ServiceAccount must exist in the installNamespace.

name follows the DNS subdomain standard as defined in [RFC 1123].
It must contain only lowercase alphanumeric characters,
hyphens (-) or periods (.), start and end with an alphanumeric character,
and be no longer than 253 characters.

Some examples of valid values are:
- some-serviceaccount
- 123-serviceaccount
- 1-serviceaccount-2
- someserviceaccount
- some.serviceaccount

Some examples of invalid values are:
- -some-serviceaccount
- some-serviceaccount-

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 253
Required: \{\}
| #### SourceConfig @@ -240,8 +240,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `sourceType` _string_ | sourceType is a required reference to the type of install source.

Allowed values are ["Catalog"]

When this field is set to "Catalog", information for determining the appropriate
bundle of content to install will be fetched from ClusterCatalog resources existing
on the cluster. When using the Catalog sourceType, the catalog field must also be set. | | Enum: [Catalog]
| -| `catalog` _[CatalogSource](#catalogsource)_ | catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog",
and must be the only field defined for this sourceType. | | | +| `sourceType` _string_ | sourceType is a required reference to the type of install source.

Allowed values are "Catalog"

When this field is set to "Catalog", information for determining the
appropriate bundle of content to install will be fetched from
ClusterCatalog resources existing on the cluster.
When using the Catalog sourceType, the catalog field must also be set. | | Enum: [Catalog]
Required: \{\}
| +| `catalog` _[CatalogSource](#catalogsource)_ | catalog is used to configure how information is sourced from a catalog.
This field is required when sourceType is "Catalog", and forbidden otherwise. | | | #### UpgradeConstraintPolicy diff --git a/internal/applier/helm.go b/internal/applier/helm.go index ac43726f17..b38a7d82a1 100644 --- a/internal/applier/helm.go +++ b/internal/applier/helm.go @@ -19,6 +19,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" apimachyaml "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" @@ -56,6 +57,26 @@ type Helm struct { Preflights []Preflight } +// shouldSkipPreflight is a helper to determine if the preflight check is CRDUpgradeSafety AND +// if it is set to enforcement None. +func shouldSkipPreflight(ctx context.Context, preflight Preflight, ext *ocv1alpha1.ClusterExtension, state string) bool { + l := log.FromContext(ctx) + hasCRDUpgradeSafety := ext.Spec.Install.Preflight != nil && ext.Spec.Install.Preflight.CRDUpgradeSafety != nil + _, isCRDUpgradeSafetyInstance := preflight.(*crdupgradesafety.Preflight) + + if hasCRDUpgradeSafety && isCRDUpgradeSafetyInstance { + if state == StateNeedsInstall || state == StateNeedsUpgrade { + l.Info("crdUpgradeSafety ", "policy", ext.Spec.Install.Preflight.CRDUpgradeSafety.Enforcement) + } + if ext.Spec.Install.Preflight.CRDUpgradeSafety.Enforcement == ocv1alpha1.CRDUpgradeSafetyEnforcementNone { + // Skip this preflight check because it is of type *crdupgradesafety.Preflight and the CRD Upgrade Safety + // policy is set to None + return true + } + } + return false +} + func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1alpha1.ClusterExtension, objectLabels map[string]string, storageLabels map[string]string) ([]client.Object, string, error) { chrt, err := convert.RegistryV1ToHelmChart(ctx, contentFS, ext.Spec.Install.Namespace, []string{corev1.NamespaceAll}) if err != nil { @@ -78,12 +99,8 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1alpha1.Clust } for _, preflight := range h.Preflights { - if ext.Spec.Install.Preflight != nil && ext.Spec.Install.Preflight.CRDUpgradeSafety != nil { - if _, ok := preflight.(*crdupgradesafety.Preflight); ok && ext.Spec.Install.Preflight.CRDUpgradeSafety.Policy == ocv1alpha1.CRDUpgradeSafetyPolicyDisabled { - // Skip this preflight check because it is of type *crdupgradesafety.Preflight and the CRD Upgrade Safety - // preflight check has been disabled - continue - } + if shouldSkipPreflight(ctx, preflight, ext, state) { + continue } switch state { case StateNeedsInstall: diff --git a/internal/conditionsets/conditionsets.go b/internal/conditionsets/conditionsets.go index fa4087148d..253e0d5ebd 100644 --- a/internal/conditionsets/conditionsets.go +++ b/internal/conditionsets/conditionsets.go @@ -16,9 +16,27 @@ limitations under the License. package conditionsets +import ( + "github.com/operator-framework/operator-controller/api/v1alpha1" +) + // ConditionTypes is the full set of ClusterExtension condition Types. // ConditionReasons is the full set of ClusterExtension condition Reasons. // -// NOTE: These are populated by init() in api/v1alpha1/clusterextension_types.go -var ConditionTypes []string -var ConditionReasons []string +// NOTE: unit tests in clusterextension_types_test will enforce completeness. +var ConditionTypes = []string{ + v1alpha1.TypeInstalled, + v1alpha1.TypeDeprecated, + v1alpha1.TypePackageDeprecated, + v1alpha1.TypeChannelDeprecated, + v1alpha1.TypeBundleDeprecated, + v1alpha1.TypeProgressing, +} + +var ConditionReasons = []string{ + v1alpha1.ReasonSucceeded, + v1alpha1.ReasonDeprecated, + v1alpha1.ReasonFailed, + v1alpha1.ReasonBlocked, + v1alpha1.ReasonRetrying, +} diff --git a/internal/controllers/clusterextension_admission_test.go b/internal/controllers/clusterextension_admission_test.go index c7bc3ae1a2..cc3ac9b2d3 100644 --- a/internal/controllers/clusterextension_admission_test.go +++ b/internal/controllers/clusterextension_admission_test.go @@ -77,7 +77,7 @@ func TestClusterExtensionSourceConfig(t *testing.T) { func TestClusterExtensionAdmissionPackageName(t *testing.T) { tooLongError := "spec.source.catalog.packageName: Too long: may not be longer than 253" - regexMismatchError := "spec.source.catalog.packageName in body should match" + regexMismatchError := "packageName must be a valid DNS1123 subdomain" testCases := []struct { name string @@ -136,7 +136,7 @@ func TestClusterExtensionAdmissionPackageName(t *testing.T) { func TestClusterExtensionAdmissionVersion(t *testing.T) { tooLongError := "spec.source.catalog.version: Too long: may not be longer than 64" - regexMismatchError := "spec.source.catalog.version in body should match" + regexMismatchError := "invalid version expression" testCases := []struct { name string @@ -236,7 +236,7 @@ func TestClusterExtensionAdmissionVersion(t *testing.T) { func TestClusterExtensionAdmissionChannel(t *testing.T) { tooLongError := "spec.source.catalog.channels[0]: Too long: may not be longer than 253" - regexMismatchError := "spec.source.catalog.channels[0] in body should match" + regexMismatchError := "channels entries must be valid DNS1123 subdomains" testCases := []struct { name string @@ -293,7 +293,7 @@ func TestClusterExtensionAdmissionChannel(t *testing.T) { func TestClusterExtensionAdmissionInstallNamespace(t *testing.T) { tooLongError := "spec.install.namespace: Too long: may not be longer than 63" - regexMismatchError := "spec.install.namespace in body should match" + regexMismatchError := "namespace must be a valid DNS1123 label" testCases := []struct { name string @@ -348,7 +348,7 @@ func TestClusterExtensionAdmissionInstallNamespace(t *testing.T) { func TestClusterExtensionAdmissionServiceAccount(t *testing.T) { tooLongError := "spec.install.serviceAccount.name: Too long: may not be longer than 253" - regexMismatchError := "spec.install.serviceAccount.name in body should match" + regexMismatchError := "name must be a valid DNS1123 subdomain" testCases := []struct { name string diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index c17390a570..47e85dd7e7 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -687,7 +687,7 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { t.Log("By checking the expected progressing conditions") progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) require.NotNil(t, progressingCond) - require.Equal(t, metav1.ConditionFalse, progressingCond.Status) + require.Equal(t, metav1.ConditionTrue, progressingCond.Status) require.Equal(t, ocv1alpha1.ReasonSucceeded, progressingCond.Reason) require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index cefe539136..1da2d8a136 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -83,14 +83,13 @@ func setInstallStatus(ext *ocv1alpha1.ClusterExtension, installStatus *ocv1alpha func setStatusProgressing(ext *ocv1alpha1.ClusterExtension, err error) { progressingCond := metav1.Condition{ Type: ocv1alpha1.TypeProgressing, - Status: metav1.ConditionFalse, + Status: metav1.ConditionTrue, Reason: ocv1alpha1.ReasonSucceeded, Message: "desired state reached", ObservedGeneration: ext.GetGeneration(), } if err != nil { - progressingCond.Status = metav1.ConditionTrue progressingCond.Reason = ocv1alpha1.ReasonRetrying progressingCond.Message = err.Error() } diff --git a/internal/controllers/common_controller_test.go b/internal/controllers/common_controller_test.go index 8f703fc6ef..40aacce725 100644 --- a/internal/controllers/common_controller_test.go +++ b/internal/controllers/common_controller_test.go @@ -22,12 +22,12 @@ func TestSetStatusProgressing(t *testing.T) { expected metav1.Condition }{ { - name: "non-nil ClusterExtension, nil error, Progressing condition has status False with reason Success", + name: "non-nil ClusterExtension, nil error, Progressing condition has status True with reason Success", err: nil, clusterExtension: &ocv1alpha1.ClusterExtension{}, expected: metav1.Condition{ Type: ocv1alpha1.TypeProgressing, - Status: metav1.ConditionFalse, + Status: metav1.ConditionTrue, Reason: ocv1alpha1.ReasonSucceeded, Message: "desired state reached", }, diff --git a/internal/resolve/catalog.go b/internal/resolve/catalog.go index 78aa7b4ae8..761448bea5 100644 --- a/internal/resolve/catalog.go +++ b/internal/resolve/catalog.go @@ -43,13 +43,18 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1alpha1.ClusterEx versionRange := ext.Spec.Source.Catalog.Version channels := ext.Spec.Source.Catalog.Channels - selector, err := metav1.LabelSelectorAsSelector(&ext.Spec.Source.Catalog.Selector) - if err != nil { - return nil, nil, nil, fmt.Errorf("desired catalog selector is invalid: %w", err) - } - // A nothing (empty) seletor selects everything - if selector == labels.Nothing() { - selector = labels.Everything() + // unless overridden, default to selecting all bundles + var selector = labels.Everything() + var err error + if ext.Spec.Source.Catalog != nil { + selector, err = metav1.LabelSelectorAsSelector(ext.Spec.Source.Catalog.Selector) + if err != nil { + return nil, nil, nil, fmt.Errorf("desired catalog selector is invalid: %w", err) + } + // A nothing (empty) selector selects everything + if selector == labels.Nothing() { + selector = labels.Everything() + } } var versionRangeConstraints *mmsemver.Constraints diff --git a/internal/resolve/catalog_test.go b/internal/resolve/catalog_test.go index 6e69c03ccd..755c767d2b 100644 --- a/internal/resolve/catalog_test.go +++ b/internal/resolve/catalog_test.go @@ -770,7 +770,7 @@ func TestInvalidClusterExtensionCatalogMatchExpressions(t *testing.T) { Source: ocv1alpha1.SourceConfig{ Catalog: &ocv1alpha1.CatalogSource{ PackageName: "foo", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { Key: "name", @@ -802,7 +802,7 @@ func TestInvalidClusterExtensionCatalogMatchLabelsName(t *testing.T) { Source: ocv1alpha1.SourceConfig{ Catalog: &ocv1alpha1.CatalogSource{ PackageName: "foo", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"": "value"}, }, }, @@ -828,7 +828,7 @@ func TestInvalidClusterExtensionCatalogMatchLabelsValue(t *testing.T) { Source: ocv1alpha1.SourceConfig{ Catalog: &ocv1alpha1.CatalogSource{ PackageName: "foo", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"name": "&value"}, }, }, @@ -852,7 +852,9 @@ func TestClusterExtensionMatchLabel(t *testing.T) { } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) - ce.Spec.Source.Catalog.Selector.MatchLabels = map[string]string{"olm.operatorframework.io/metadata.name": "b"} + ce.Spec.Source.Catalog.Selector = &metav1.LabelSelector{ + MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": "b"}, + } _, _, _, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) @@ -871,7 +873,9 @@ func TestClusterExtensionNoMatchLabel(t *testing.T) { } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) - ce.Spec.Source.Catalog.Selector.MatchLabels = map[string]string{"olm.operatorframework.io/metadata.name": "a"} + ce.Spec.Source.Catalog.Selector = &metav1.LabelSelector{ + MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": "a"}, + } _, _, _, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index c141ff0ca6..b1d8471ac0 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -313,7 +313,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: tc.packageName, - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, }, @@ -334,12 +334,12 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) }, pollDuration, pollInterval) - t.Log("By eventually reporting no longer progressing") + t.Log("By eventually reporting progressing as True") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -440,7 +440,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -496,7 +496,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -512,7 +512,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -547,7 +547,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -562,7 +562,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -580,7 +580,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: "prometheus", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { Key: "olm.operatorframework.io/metadata.name", @@ -607,7 +607,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -631,7 +631,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -669,7 +669,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: "prometheus", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, }, @@ -690,7 +690,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -714,7 +714,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -732,7 +732,7 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: "prometheus", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, }, @@ -797,7 +797,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: "prometheus", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, }, @@ -858,12 +858,12 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes } }, pollDuration, pollInterval) - t.Log("By eventually reporting Progressing == False with Reason Success") + t.Log("By eventually reporting Progressing == True with Reason Success") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) From aaa0e0090ec0a04bbc990bbda0a4146852cf70bf Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Mon, 11 Nov 2024 06:12:27 -0500 Subject: [PATCH 114/694] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20Move=20`.spec.inst?= =?UTF-8?q?all.namespace`=20and=20`.spec.install.serviceAccount`=20to=20`.?= =?UTF-8?q?spec.namespace`=20and=20`.spec.serviceAccount`=20(#1439)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * move namespace and serviceAccount fields to spec root Signed-off-by: everettraven * address comments, make verify Signed-off-by: everettraven * fix e2e failures Signed-off-by: everettraven --------- Signed-off-by: everettraven --- api/v1alpha1/clusterextension_types.go | 74 ++++----- api/v1alpha1/zz_generated.deepcopy.go | 8 +- cmd/manager/main.go | 2 +- ...peratorframework.io_clusterextensions.yaml | 146 ++++++++---------- .../olm_v1alpha1_clusterextension.yaml | 7 +- .../operator-controller-api-reference.md | 8 +- internal/action/restconfig.go | 4 +- internal/applier/helm.go | 12 +- .../clusterextension_admission_test.go | 120 +++++++++----- .../clusterextension_controller_test.go | 78 ++++------ internal/resolve/catalog_test.go | 6 +- test/e2e/cluster_extension_install_test.go | 74 ++++----- .../extension_developer_test.go | 8 +- 13 files changed, 270 insertions(+), 277 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index c0fba00981..8ccdb66c5e 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -43,6 +43,34 @@ const ( // ClusterExtensionSpec defines the desired state of ClusterExtension type ClusterExtensionSpec struct { + // namespace is a reference to a Kubernetes namespace. + // This is the namespace in which the provided ServiceAccount must exist. + // It also designates the default namespace where namespace-scoped resources + // for the extension are applied to the cluster. + // Some extensions may contain namespace-scoped resources to be applied in other namespaces. + // This namespace must exist. + // + // namespace is required, immutable, and follows the DNS label standard + // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-), + // start and end with an alphanumeric character, and be no longer than 63 characters + // + // [RFC 1123]: https://tools.ietf.org/html/rfc1123 + // + // +kubebuilder:validation:MaxLength:=63 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="namespace is immutable" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$\")",message="namespace must be a valid DNS1123 label" + // +kubebuilder:validation:Required + Namespace string `json:"namespace"` + + // serviceAccount is a reference to a ServiceAccount used to perform all interactions + // with the cluster that are required to manage the extension. + // The ServiceAccount must be configured with the necessary permissions to perform these interactions. + // The ServiceAccount must exist in the namespace referenced in the spec. + // serviceAccount is required. + // + // +kubebuilder:validation:Required + ServiceAccount ServiceAccountReference `json:"serviceAccount"` + // source is a required field which selects the installation source of content // for this ClusterExtension. Selection is performed by setting the sourceType. // @@ -59,18 +87,11 @@ type ClusterExtensionSpec struct { // +kubebuilder:validation:Required Source SourceConfig `json:"source"` - // install is a required field used to configure the installation options - // for the ClusterExtension such as the installation namespace, - // the service account and the pre-flight check configuration. + // install is an optional field used to configure the installation options + // for the ClusterExtension such as the pre-flight check configuration. // - // Below is a minimal example of an installation definition (in yaml): - // install: - // namespace: example-namespace - // serviceAccount: - // name: example-sa - // - // +kubebuilder:validation:Required - Install ClusterExtensionInstallConfig `json:"install"` + // +optional + Install *ClusterExtensionInstallConfig `json:"install,omitempty"` } const SourceTypeCatalog = "Catalog" @@ -104,38 +125,9 @@ type SourceConfig struct { // ClusterExtensionInstallConfig is a union which selects the clusterExtension installation config. // ClusterExtensionInstallConfig requires the namespace and serviceAccount which should be used for the installation of packages. // +// +kubebuilder:validation:XValidation:rule="has(self.preflight)",message="at least one of [preflight] are required when install is specified" // +union type ClusterExtensionInstallConfig struct { - // namespace designates the kubernetes Namespace where bundle content - // for the package, referenced in the 'packageName' field, will be applied and the necessary - // service account can be found. - // The bundle may contain cluster-scoped resources or resources that are - // applied to other Namespaces. This Namespace is expected to exist. - // - // namespace is required, immutable, and follows the DNS label standard - // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-), - // start and end with an alphanumeric character, and be no longer than 63 characters - // - // [RFC 1123]: https://tools.ietf.org/html/rfc1123 - // - // +kubebuilder:validation:MaxLength:=63 - // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="namespace is immutable" - // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$\")",message="namespace must be a valid DNS1123 label. It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character, and be no longer than 63 characters" - // +kubebuilder:validation:Required - Namespace string `json:"namespace"` - - // serviceAccount is a required reference to a ServiceAccount that exists - // in the installNamespace which is used to install and - // manage the content for the package specified in the packageName field. - // - // In order to successfully install and manage the content for the package, - // the ServiceAccount provided via this field should be configured with the - // appropriate permissions to perform the necessary operations on all the - // resources that are included in the bundle of content being applied. - // - // +kubebuilder:validation:Required - ServiceAccount ServiceAccountReference `json:"serviceAccount"` - // preflight is an optional field that can be used to configure the checks that are // run before installation or upgrade of the content for the package specified in the packageName field. // diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 7ca0e77848..e3b9b03366 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -110,7 +110,6 @@ func (in *ClusterExtension) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterExtensionInstallConfig) DeepCopyInto(out *ClusterExtensionInstallConfig) { *out = *in - out.ServiceAccount = in.ServiceAccount if in.Preflight != nil { in, out := &in.Preflight, &out.Preflight *out = new(PreflightConfig) @@ -179,8 +178,13 @@ func (in *ClusterExtensionList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterExtensionSpec) DeepCopyInto(out *ClusterExtensionSpec) { *out = *in + out.ServiceAccount = in.ServiceAccount in.Source.DeepCopyInto(&out.Source) - in.Install.DeepCopyInto(&out.Install) + if in.Install != nil { + in, out := &in.Install, &out.Install + *out = new(ClusterExtensionInstallConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionSpec. diff --git a/cmd/manager/main.go b/cmd/manager/main.go index c353a4cb06..2efadb2a0c 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -197,7 +197,7 @@ func main() { helmclient.StorageDriverMapper(action.ChunkedStorageDriverMapper(coreClient, mgr.GetAPIReader(), systemNamespace)), helmclient.ClientNamespaceMapper(func(obj client.Object) (string, error) { ext := obj.(*ocv1alpha1.ClusterExtension) - return ext.Spec.Install.Namespace, nil + return ext.Spec.Namespace, nil }), helmclient.ClientRestConfigMapper(clientRestConfigMapper), ) diff --git a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml index 106605424c..ef946c56a4 100644 --- a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml +++ b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -58,39 +58,9 @@ spec: properties: install: description: |- - install is a required field used to configure the installation options - for the ClusterExtension such as the installation namespace, - the service account and the pre-flight check configuration. - - Below is a minimal example of an installation definition (in yaml): - install: - namespace: example-namespace - serviceAccount: - name: example-sa + install is an optional field used to configure the installation options + for the ClusterExtension such as the pre-flight check configuration. properties: - namespace: - description: |- - namespace designates the kubernetes Namespace where bundle content - for the package, referenced in the 'packageName' field, will be applied and the necessary - service account can be found. - The bundle may contain cluster-scoped resources or resources that are - applied to other Namespaces. This Namespace is expected to exist. - - namespace is required, immutable, and follows the DNS label standard - as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-), - start and end with an alphanumeric character, and be no longer than 63 characters - - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 63 - type: string - x-kubernetes-validations: - - message: namespace is immutable - rule: self == oldSelf - - message: namespace must be a valid DNS1123 label. It must contain - only lowercase alphanumeric characters or hyphens (-), start - and end with an alphanumeric character, and be no longer than - 63 characters - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") preflight: description: |- preflight is an optional field that can be used to configure the checks that are @@ -133,58 +103,77 @@ spec: - message: at least one of [crdUpgradeSafety] are required when preflight is specified rule: has(self.crdUpgradeSafety) - serviceAccount: + type: object + x-kubernetes-validations: + - message: at least one of [preflight] are required when install is + specified + rule: has(self.preflight) + namespace: + description: |- + namespace is a reference to a Kubernetes namespace. + This is the namespace in which the provided ServiceAccount must exist. + It also designates the default namespace where namespace-scoped resources + for the extension are applied to the cluster. + Some extensions may contain namespace-scoped resources to be applied in other namespaces. + This namespace must exist. + + namespace is required, immutable, and follows the DNS label standard + as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-), + start and end with an alphanumeric character, and be no longer than 63 characters + + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 63 + type: string + x-kubernetes-validations: + - message: namespace is immutable + rule: self == oldSelf + - message: namespace must be a valid DNS1123 label + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") + serviceAccount: + description: |- + serviceAccount is a reference to a ServiceAccount used to perform all interactions + with the cluster that are required to manage the extension. + The ServiceAccount must be configured with the necessary permissions to perform these interactions. + The ServiceAccount must exist in the namespace referenced in the spec. + serviceAccount is required. + properties: + name: description: |- - serviceAccount is a required reference to a ServiceAccount that exists - in the installNamespace which is used to install and - manage the content for the package specified in the packageName field. - - In order to successfully install and manage the content for the package, - the ServiceAccount provided via this field should be configured with the - appropriate permissions to perform the necessary operations on all the - resources that are included in the bundle of content being applied. - properties: - name: - description: |- - name is a required, immutable reference to the name of the ServiceAccount - to be used for installation and management of the content for the package - specified in the packageName field. + name is a required, immutable reference to the name of the ServiceAccount + to be used for installation and management of the content for the package + specified in the packageName field. - This ServiceAccount must exist in the installNamespace. + This ServiceAccount must exist in the installNamespace. - name follows the DNS subdomain standard as defined in [RFC 1123]. - It must contain only lowercase alphanumeric characters, - hyphens (-) or periods (.), start and end with an alphanumeric character, - and be no longer than 253 characters. + name follows the DNS subdomain standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric character, + and be no longer than 253 characters. - Some examples of valid values are: - - some-serviceaccount - - 123-serviceaccount - - 1-serviceaccount-2 - - someserviceaccount - - some.serviceaccount + Some examples of valid values are: + - some-serviceaccount + - 123-serviceaccount + - 1-serviceaccount-2 + - someserviceaccount + - some.serviceaccount - Some examples of invalid values are: - - -some-serviceaccount - - some-serviceaccount- + Some examples of invalid values are: + - -some-serviceaccount + - some-serviceaccount- - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 253 - type: string - x-kubernetes-validations: - - message: name is immutable - rule: self == oldSelf - - message: name must be a valid DNS1123 subdomain. It must - contain only lowercase alphanumeric characters, hyphens - (-) or periods (.), start and end with an alphanumeric - character, and be no longer than 253 characters - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") - required: - - name - type: object + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 253 + type: string + x-kubernetes-validations: + - message: name is immutable + rule: self == oldSelf + - message: name must be a valid DNS1123 subdomain. It must contain + only lowercase alphanumeric characters, hyphens (-) or periods + (.), start and end with an alphanumeric character, and be + no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") required: - - namespace - - serviceAccount + - name type: object source: description: |- @@ -468,7 +457,8 @@ spec: rule: 'has(self.sourceType) && self.sourceType == ''Catalog'' ? has(self.catalog) : !has(self.catalog)' required: - - install + - namespace + - serviceAccount - source type: object status: diff --git a/config/samples/olm_v1alpha1_clusterextension.yaml b/config/samples/olm_v1alpha1_clusterextension.yaml index 7536c3d90e..f9bc5ff10f 100644 --- a/config/samples/olm_v1alpha1_clusterextension.yaml +++ b/config/samples/olm_v1alpha1_clusterextension.yaml @@ -272,12 +272,11 @@ kind: ClusterExtension metadata: name: argocd spec: + namespace: argocd + serviceAccount: + name: argocd-installer source: sourceType: Catalog catalog: packageName: argocd-operator version: 0.6.0 - install: - namespace: argocd - serviceAccount: - name: argocd-installer diff --git a/docs/api-reference/operator-controller-api-reference.md b/docs/api-reference/operator-controller-api-reference.md index f6553a7a44..8977d3a3a6 100644 --- a/docs/api-reference/operator-controller-api-reference.md +++ b/docs/api-reference/operator-controller-api-reference.md @@ -120,8 +120,6 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `namespace` _string_ | namespace designates the kubernetes Namespace where bundle content
for the package, referenced in the 'packageName' field, will be applied and the necessary
service account can be found.
The bundle may contain cluster-scoped resources or resources that are
applied to other Namespaces. This Namespace is expected to exist.

namespace is required, immutable, and follows the DNS label standard
as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-),
start and end with an alphanumeric character, and be no longer than 63 characters

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 63
Required: \{\}
| -| `serviceAccount` _[ServiceAccountReference](#serviceaccountreference)_ | serviceAccount is a required reference to a ServiceAccount that exists
in the installNamespace which is used to install and
manage the content for the package specified in the packageName field.

In order to successfully install and manage the content for the package,
the ServiceAccount provided via this field should be configured with the
appropriate permissions to perform the necessary operations on all the
resources that are included in the bundle of content being applied. | | Required: \{\}
| | `preflight` _[PreflightConfig](#preflightconfig)_ | preflight is an optional field that can be used to configure the checks that are
run before installation or upgrade of the content for the package specified in the packageName field.

When specified, it replaces the default preflight configuration for install/upgrade actions.
When not specified, the default configuration will be used. | | | @@ -174,8 +172,10 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | +| `namespace` _string_ | namespace is a reference to a Kubernetes namespace.
This is the namespace in which the provided ServiceAccount must exist.
It also designates the default namespace where namespace-scoped resources
for the extension are applied to the cluster.
Some extensions may contain namespace-scoped resources to be applied in other namespaces.
This namespace must exist.

namespace is required, immutable, and follows the DNS label standard
as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-),
start and end with an alphanumeric character, and be no longer than 63 characters

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 63
Required: \{\}
| +| `serviceAccount` _[ServiceAccountReference](#serviceaccountreference)_ | serviceAccount is a reference to a ServiceAccount used to perform all interactions
with the cluster that are required to manage the extension.
The ServiceAccount must be configured with the necessary permissions to perform these interactions.
The ServiceAccount must exist in the namespace referenced in the spec.
serviceAccount is required. | | Required: \{\}
| | `source` _[SourceConfig](#sourceconfig)_ | source is a required field which selects the installation source of content
for this ClusterExtension. Selection is performed by setting the sourceType.

Catalog is currently the only implemented sourceType, and setting the
sourcetype to "Catalog" requires the catalog field to also be defined.

Below is a minimal example of a source definition (in yaml):

source:
sourceType: Catalog
catalog:
packageName: example-package | | Required: \{\}
| -| `install` _[ClusterExtensionInstallConfig](#clusterextensioninstallconfig)_ | install is a required field used to configure the installation options
for the ClusterExtension such as the installation namespace,
the service account and the pre-flight check configuration.

Below is a minimal example of an installation definition (in yaml):
install:
namespace: example-namespace
serviceAccount:
name: example-sa | | Required: \{\}
| +| `install` _[ClusterExtensionInstallConfig](#clusterextensioninstallconfig)_ | install is an optional field used to configure the installation options
for the ClusterExtension such as the pre-flight check configuration. | | | #### ClusterExtensionStatus @@ -220,7 +220,7 @@ ServiceAccountReference identifies the serviceAccount used fo install a ClusterE _Appears in:_ -- [ClusterExtensionInstallConfig](#clusterextensioninstallconfig) +- [ClusterExtensionSpec](#clusterextensionspec) | Field | Description | Default | Validation | | --- | --- | --- | --- | diff --git a/internal/action/restconfig.go b/internal/action/restconfig.go index a034ebb211..d01e302add 100644 --- a/internal/action/restconfig.go +++ b/internal/action/restconfig.go @@ -16,8 +16,8 @@ func ServiceAccountRestConfigMapper(tokenGetter *authentication.TokenGetter) fun return func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) { cExt := o.(*ocv1alpha1.ClusterExtension) saKey := types.NamespacedName{ - Name: cExt.Spec.Install.ServiceAccount.Name, - Namespace: cExt.Spec.Install.Namespace, + Name: cExt.Spec.ServiceAccount.Name, + Namespace: cExt.Spec.Namespace, } saConfig := rest.AnonymousClientConfig(c) saConfig.Wrap(func(rt http.RoundTripper) http.RoundTripper { diff --git a/internal/applier/helm.go b/internal/applier/helm.go index b38a7d82a1..9f288afa99 100644 --- a/internal/applier/helm.go +++ b/internal/applier/helm.go @@ -61,7 +61,7 @@ type Helm struct { // if it is set to enforcement None. func shouldSkipPreflight(ctx context.Context, preflight Preflight, ext *ocv1alpha1.ClusterExtension, state string) bool { l := log.FromContext(ctx) - hasCRDUpgradeSafety := ext.Spec.Install.Preflight != nil && ext.Spec.Install.Preflight.CRDUpgradeSafety != nil + hasCRDUpgradeSafety := ext.Spec.Install != nil && ext.Spec.Install.Preflight != nil && ext.Spec.Install.Preflight.CRDUpgradeSafety != nil _, isCRDUpgradeSafetyInstance := preflight.(*crdupgradesafety.Preflight) if hasCRDUpgradeSafety && isCRDUpgradeSafetyInstance { @@ -78,7 +78,7 @@ func shouldSkipPreflight(ctx context.Context, preflight Preflight, ext *ocv1alph } func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1alpha1.ClusterExtension, objectLabels map[string]string, storageLabels map[string]string) ([]client.Object, string, error) { - chrt, err := convert.RegistryV1ToHelmChart(ctx, contentFS, ext.Spec.Install.Namespace, []string{corev1.NamespaceAll}) + chrt, err := convert.RegistryV1ToHelmChart(ctx, contentFS, ext.Spec.Namespace, []string{corev1.NamespaceAll}) if err != nil { return nil, "", err } @@ -118,7 +118,7 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1alpha1.Clust switch state { case StateNeedsInstall: - rel, err = ac.Install(ext.GetName(), ext.Spec.Install.Namespace, chrt, values, func(install *action.Install) error { + rel, err = ac.Install(ext.GetName(), ext.Spec.Namespace, chrt, values, func(install *action.Install) error { install.CreateNamespace = false install.Labels = storageLabels return nil @@ -127,7 +127,7 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1alpha1.Clust return nil, state, err } case StateNeedsUpgrade: - rel, err = ac.Upgrade(ext.GetName(), ext.Spec.Install.Namespace, chrt, values, func(upgrade *action.Upgrade) error { + rel, err = ac.Upgrade(ext.GetName(), ext.Spec.Namespace, chrt, values, func(upgrade *action.Upgrade) error { upgrade.MaxHistory = maxHelmReleaseHistory upgrade.Labels = storageLabels return nil @@ -161,7 +161,7 @@ func (h *Helm) getReleaseState(cl helmclient.ActionInterface, ext *ocv1alpha1.Cl } if errors.Is(err, driver.ErrReleaseNotFound) { - desiredRelease, err := cl.Install(ext.GetName(), ext.Spec.Install.Namespace, chrt, values, func(i *action.Install) error { + desiredRelease, err := cl.Install(ext.GetName(), ext.Spec.Namespace, chrt, values, func(i *action.Install) error { i.DryRun = true i.DryRunOption = "server" return nil @@ -171,7 +171,7 @@ func (h *Helm) getReleaseState(cl helmclient.ActionInterface, ext *ocv1alpha1.Cl } return nil, desiredRelease, StateNeedsInstall, nil } - desiredRelease, err := cl.Upgrade(ext.GetName(), ext.Spec.Install.Namespace, chrt, values, func(upgrade *action.Upgrade) error { + desiredRelease, err := cl.Upgrade(ext.GetName(), ext.Spec.Namespace, chrt, values, func(upgrade *action.Upgrade) error { upgrade.MaxHistory = maxHelmReleaseHistory upgrade.DryRun = true upgrade.DryRunOption = "server" diff --git a/internal/controllers/clusterextension_admission_test.go b/internal/controllers/clusterextension_admission_test.go index cc3ac9b2d3..9c180533cd 100644 --- a/internal/controllers/clusterextension_admission_test.go +++ b/internal/controllers/clusterextension_admission_test.go @@ -43,11 +43,9 @@ func TestClusterExtensionSourceConfig(t *testing.T) { PackageName: "test-package", }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: "default", - }, + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: "default", }, })) } @@ -56,11 +54,9 @@ func TestClusterExtensionSourceConfig(t *testing.T) { Source: ocv1alpha1.SourceConfig{ SourceType: tc.sourceType, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: "default", - }, + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: "default", }, })) } @@ -117,11 +113,9 @@ func TestClusterExtensionAdmissionPackageName(t *testing.T) { PackageName: tc.pkgName, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: "default", - }, + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: "default", }, })) if tc.errMsg == "" { @@ -217,11 +211,9 @@ func TestClusterExtensionAdmissionVersion(t *testing.T) { Version: tc.version, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: "default", - }, + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: "default", }, })) if tc.errMsg == "" { @@ -274,11 +266,9 @@ func TestClusterExtensionAdmissionChannel(t *testing.T) { Channels: tc.channels, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: "default", - }, + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: "default", }, })) if tc.errMsg == "" { @@ -292,7 +282,7 @@ func TestClusterExtensionAdmissionChannel(t *testing.T) { } func TestClusterExtensionAdmissionInstallNamespace(t *testing.T) { - tooLongError := "spec.install.namespace: Too long: may not be longer than 63" + tooLongError := "spec.namespace: Too long: may not be longer than 63" regexMismatchError := "namespace must be a valid DNS1123 label" testCases := []struct { @@ -329,11 +319,9 @@ func TestClusterExtensionAdmissionInstallNamespace(t *testing.T) { PackageName: "package", }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: tc.namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: "default", - }, + Namespace: tc.namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: "default", }, })) if tc.errMsg == "" { @@ -347,7 +335,7 @@ func TestClusterExtensionAdmissionInstallNamespace(t *testing.T) { } func TestClusterExtensionAdmissionServiceAccount(t *testing.T) { - tooLongError := "spec.install.serviceAccount.name: Too long: may not be longer than 253" + tooLongError := "spec.serviceAccount.name: Too long: may not be longer than 253" regexMismatchError := "name must be a valid DNS1123 subdomain" testCases := []struct { @@ -385,11 +373,9 @@ func TestClusterExtensionAdmissionServiceAccount(t *testing.T) { PackageName: "package", }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: tc.serviceAccount, - }, + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: tc.serviceAccount, }, })) if tc.errMsg == "" { @@ -402,6 +388,66 @@ func TestClusterExtensionAdmissionServiceAccount(t *testing.T) { } } +func TestClusterExtensionAdmissionInstall(t *testing.T) { + oneOfErrMsg := "at least one of [preflight] are required when install is specified" + + testCases := []struct { + name string + installConfig *ocv1alpha1.ClusterExtensionInstallConfig + errMsg string + }{ + { + name: "install specified, nothing configured", + installConfig: &ocv1alpha1.ClusterExtensionInstallConfig{}, + errMsg: oneOfErrMsg, + }, + { + name: "install specified, preflight configured", + installConfig: &ocv1alpha1.ClusterExtensionInstallConfig{ + Preflight: &ocv1alpha1.PreflightConfig{ + CRDUpgradeSafety: &ocv1alpha1.CRDUpgradeSafetyPreflightConfig{ + Enforcement: ocv1alpha1.CRDUpgradeSafetyEnforcementNone, + }, + }, + }, + errMsg: "", + }, + { + name: "install not specified", + installConfig: nil, + errMsg: "", + }, + } + + t.Parallel() + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + cl := newClient(t) + err := cl.Create(context.Background(), buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ + Source: ocv1alpha1.SourceConfig{ + SourceType: "Catalog", + Catalog: &ocv1alpha1.CatalogSource{ + PackageName: "package", + }, + }, + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: "default", + }, + Install: tc.installConfig, + })) + if tc.errMsg == "" { + require.NoError(t, err, "unexpected error for install configuration %v: %w", tc.installConfig, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errMsg) + } + }) + } +} + func buildClusterExtension(spec ocv1alpha1.ClusterExtensionSpec) *ocv1alpha1.ClusterExtension { return &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index 47e85dd7e7..448327944b 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -67,11 +67,9 @@ func TestClusterExtensionResolutionFails(t *testing.T) { PackageName: pkgName, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: "default", - }, + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: "default", }, }, } @@ -145,11 +143,9 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { Channels: []string{pkgChan}, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, }, }, } @@ -229,11 +225,9 @@ func TestClusterExtensionUnpackUnexpectedState(t *testing.T) { Channels: []string{pkgChan}, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, }, }, } @@ -289,11 +283,9 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) Channels: []string{pkgChan}, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, }, }, } @@ -371,11 +363,9 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { Channels: []string{pkgChan}, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, }, }, } @@ -472,11 +462,9 @@ func TestClusterExtensionManagerFailed(t *testing.T) { Channels: []string{pkgChan}, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, }, }, } @@ -556,11 +544,9 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { Channels: []string{pkgChan}, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: installNamespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, + Namespace: installNamespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, }, }, } @@ -641,11 +627,9 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { Channels: []string{pkgChan}, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, }, }, } @@ -724,11 +708,9 @@ func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { Channels: []string{pkgChan}, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: serviceAccount, - }, + Namespace: namespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: serviceAccount, }, }, } @@ -1488,7 +1470,8 @@ func TestGetInstalledBundleHistory(t *testing.T) { labels.BundleReferenceKey: "bundle-ref", }, }, - }, nil, + }, + nil, &controllers.InstalledBundle{ BundleMetadata: ocv1alpha1.BundleMetadata{ Name: "test-ext", @@ -1522,7 +1505,8 @@ func TestGetInstalledBundleHistory(t *testing.T) { labels.BundleReferenceKey: "bundle-ref-1", }, }, - }, nil, + }, + nil, &controllers.InstalledBundle{ BundleMetadata: ocv1alpha1.BundleMetadata{ Name: "test-ext", diff --git a/internal/resolve/catalog_test.go b/internal/resolve/catalog_test.go index 755c767d2b..ba6a70027f 100644 --- a/internal/resolve/catalog_test.go +++ b/internal/resolve/catalog_test.go @@ -646,10 +646,8 @@ func buildFooClusterExtension(pkg string, channels []string, version string, upg Name: pkg, }, Spec: ocv1alpha1.ClusterExtensionSpec{ - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{Name: "default"}, - }, + Namespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{Name: "default"}, Source: ocv1alpha1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index b1d8471ac0..2a93a1de4e 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -318,11 +318,9 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { }, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: ns.Name, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, } t.Log("It resolves the specified package with correct bundle path") @@ -373,11 +371,9 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { PackageName: "prometheus", }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: ns.Name, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, } t.Log("It resolves to multiple bundle paths") @@ -419,11 +415,9 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { // No Selector since this is an exact version match }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: ns.Name, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, } require.NoError(t, c.Create(context.Background(), clusterExtension)) @@ -483,11 +477,9 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { Version: "1.0.0", }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: ns.Name, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, } require.NoError(t, c.Create(context.Background(), clusterExtension)) @@ -534,11 +526,9 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { Version: "1.0.0", }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: ns.Name, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, } require.NoError(t, c.Create(context.Background(), clusterExtension)) @@ -591,11 +581,9 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { }, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: ns.Name, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, } t.Log("It resolves the specified package with correct bundle path") @@ -674,11 +662,9 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { }, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: ns.Name, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, } t.Log("It resolves the specified package with correct bundle path") @@ -737,11 +723,9 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T }, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: ns.Name, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, } t.Log("It installs the specified package with correct bundle path") @@ -763,7 +747,7 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T prometheusService := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "prometheus-operator", - Namespace: clusterExtension.Spec.Install.Namespace, + Namespace: clusterExtension.Spec.Namespace, }, } require.NoError(t, c.Delete(context.Background(), prometheusService)) @@ -802,11 +786,9 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes }, }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: ns.Name, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, } t.Log("It resolves the specified package with correct bundle path") diff --git a/test/extension-developer-e2e/extension_developer_test.go b/test/extension-developer-e2e/extension_developer_test.go index 4a11a75300..bb6bdad395 100644 --- a/test/extension-developer-e2e/extension_developer_test.go +++ b/test/extension-developer-e2e/extension_developer_test.go @@ -75,11 +75,9 @@ func TestExtensionDeveloper(t *testing.T) { PackageName: os.Getenv("REG_PKG_NAME"), }, }, - Install: ocv1alpha1.ClusterExtensionInstallConfig{ - Namespace: installNamespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: sa.Name, - }, + Namespace: installNamespace, + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: sa.Name, }, }, } From 046c3df3c87f642d3a08f35c6159694318c97d3d Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:30:30 +0100 Subject: [PATCH 115/694] Fix upgrade-e2e CI job (#1445) There were several intentional breaking changes in the API which are now included in v0.18.0 release. Our upgrade-e2e job need to be updated to make take the changes into account. Relevant PRs: * https://github.com/operator-framework/operator-controller/pull/1439 * https://github.com/operator-framework/operator-controller/pull/1434 Signed-off-by: Mikalai Radchuk --- config/samples/catalogd_operatorcatalog.yaml | 2 +- hack/test/pre-upgrade-setup.sh | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/config/samples/catalogd_operatorcatalog.yaml b/config/samples/catalogd_operatorcatalog.yaml index 48f1da5734..8657f7a702 100644 --- a/config/samples/catalogd_operatorcatalog.yaml +++ b/config/samples/catalogd_operatorcatalog.yaml @@ -7,4 +7,4 @@ spec: type: Image image: ref: quay.io/operatorhubio/catalog:latest - pollInterval: 10m + pollIntervalMinutes: 10 diff --git a/hack/test/pre-upgrade-setup.sh b/hack/test/pre-upgrade-setup.sh index 00734f9527..6256944ac0 100755 --- a/hack/test/pre-upgrade-setup.sh +++ b/hack/test/pre-upgrade-setup.sh @@ -29,7 +29,7 @@ spec: type: Image image: ref: ${TEST_CATALOG_IMG} - pollInterval: 24h + pollIntervalMinutes: 1440 EOF kubectl apply -f - < Date: Tue, 12 Nov 2024 21:45:40 +0100 Subject: [PATCH 116/694] :warning: Bump ClusterExtension API to v1 (#1228) * :warning: bump ClusterExtension API to v1 Signed-off-by: Per Goncalves da Silva * CE API updates - Regenerate CRD - Fixup import in conditionsets package Signed-off-by: Joe Lanford * update imports to drop alpha1 suffix and other minor version changes that were missed when bumping API versions to v1. Signed-off-by: everettraven --------- Signed-off-by: Per Goncalves da Silva Signed-off-by: Joe Lanford Signed-off-by: everettraven Co-authored-by: Per Goncalves da Silva Co-authored-by: Joe Lanford Co-authored-by: everettraven --- PROJECT | 8 +- .../clusterextension_types.go | 2 +- .../clusterextension_types_test.go | 2 +- api/{v1alpha1 => v1}/groupversion_info.go | 4 +- api/{v1alpha1 => v1}/zz_generated.deepcopy.go | 8 +- cmd/manager/main.go | 10 +- ...peratorframework.io_clusterextensions.yaml | 2 +- config/samples/kustomization.yaml | 2 +- ...sion.yaml => olm_v1_clusterextension.yaml} | 2 +- .../operator-controller-api-reference.md | 8 +- .../concepts/controlling-catalog-selection.md | 12 +- docs/concepts/crd-upgrade-safety.md | 2 +- docs/concepts/upgrade-support.md | 4 +- docs/howto/how-to-channel-based-upgrades.md | 4 +- docs/howto/how-to-pin-version.md | 2 +- docs/howto/how-to-version-range-upgrades.md | 2 +- docs/howto/how-to-z-stream-upgrades.md | 2 +- docs/tutorials/downgrade-extension.md | 6 +- docs/tutorials/install-extension.md | 4 +- docs/tutorials/upgrade-extension.md | 8 +- hack/tools/catalogs/lib/manifests.sh | 2 +- internal/action/restconfig.go | 4 +- internal/applier/helm.go | 10 +- internal/bundleutil/bundle.go | 6 +- internal/catalogmetadata/filter/successors.go | 10 +- .../catalogmetadata/filter/successors_test.go | 12 +- internal/conditionsets/conditionsets.go | 24 +- internal/contentmanager/cache/cache_test.go | 18 +- internal/contentmanager/contentmanager.go | 12 +- .../source/dynamicsource_test.go | 4 +- internal/contentmanager/sourcerer.go | 4 +- .../clusterextension_admission_test.go | 80 +-- .../clusterextension_controller.go | 64 +-- .../clusterextension_controller_test.go | 494 +++++++++--------- internal/controllers/common_controller.go | 36 +- .../controllers/common_controller_test.go | 24 +- internal/controllers/suite_test.go | 12 +- internal/resolve/catalog.go | 8 +- internal/resolve/catalog_test.go | 114 ++-- internal/resolve/resolver.go | 8 +- internal/scheme/scheme.go | 4 +- test/e2e/cluster_extension_install_test.go | 162 +++--- .../extension_developer_test.go | 20 +- test/upgrade-e2e/post_upgrade_test.go | 14 +- 44 files changed, 620 insertions(+), 620 deletions(-) rename api/{v1alpha1 => v1}/clusterextension_types.go (99%) rename api/{v1alpha1 => v1}/clusterextension_types_test.go (99%) rename api/{v1alpha1 => v1}/groupversion_info.go (96%) rename api/{v1alpha1 => v1}/zz_generated.deepcopy.go (98%) rename config/samples/{olm_v1alpha1_clusterextension.yaml => olm_v1_clusterextension.yaml} (99%) diff --git a/PROJECT b/PROJECT index 50ac542dca..a307347a46 100644 --- a/PROJECT +++ b/PROJECT @@ -11,8 +11,8 @@ resources: domain: operatorframework.io group: olm kind: ClusterExtension - path: github.com/operator-framework/operator-controller/api/v1alpha1 - version: v1alpha1 + path: github.com/operator-framework/operator-controller/api/v1 + version: v1 - api: crdVersion: v1 namespaced: true @@ -20,6 +20,6 @@ resources: domain: operatorframework.io group: olm kind: Extension - path: github.com/operator-framework/operator-controller/api/v1alpha1 - version: v1alpha1 + path: github.com/operator-framework/operator-controller/api/v1 + version: v1 version: "3" diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1/clusterextension_types.go similarity index 99% rename from api/v1alpha1/clusterextension_types.go rename to api/v1/clusterextension_types.go index 8ccdb66c5e..696966c5a3 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1/clusterextension_types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1alpha1 +package v1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/api/v1alpha1/clusterextension_types_test.go b/api/v1/clusterextension_types_test.go similarity index 99% rename from api/v1alpha1/clusterextension_types_test.go rename to api/v1/clusterextension_types_test.go index 0ed4f1a088..297a15b132 100644 --- a/api/v1alpha1/clusterextension_types_test.go +++ b/api/v1/clusterextension_types_test.go @@ -1,4 +1,4 @@ -package v1alpha1_test +package v1_test import ( "fmt" diff --git a/api/v1alpha1/groupversion_info.go b/api/v1/groupversion_info.go similarity index 96% rename from api/v1alpha1/groupversion_info.go rename to api/v1/groupversion_info.go index f46abbf3da..fdd9174aca 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1/groupversion_info.go @@ -17,7 +17,7 @@ limitations under the License. // Package v1alpha1 contains API Schema definitions for the olm v1alpha1 API group // +kubebuilder:object:generate=true // +groupName=olm.operatorframework.io -package v1alpha1 +package v1 import ( "k8s.io/apimachinery/pkg/runtime/schema" @@ -26,7 +26,7 @@ import ( var ( // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "olm.operatorframework.io", Version: "v1alpha1"} + GroupVersion = schema.GroupVersion{Group: "olm.operatorframework.io", Version: "v1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go similarity index 98% rename from api/v1alpha1/zz_generated.deepcopy.go rename to api/v1/zz_generated.deepcopy.go index e3b9b03366..622bd2b834 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -18,10 +18,10 @@ limitations under the License. // Code generated by controller-gen. DO NOT EDIT. -package v1alpha1 +package v1 import ( - "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -65,7 +65,7 @@ func (in *CatalogSource) DeepCopyInto(out *CatalogSource) { } if in.Selector != nil { in, out := &in.Selector, &out.Selector - *out = new(v1.LabelSelector) + *out = new(metav1.LabelSelector) (*in).DeepCopyInto(*out) } } @@ -202,7 +202,7 @@ func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 2efadb2a0c..563e514795 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -49,7 +49,7 @@ import ( catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/action" "github.com/operator-framework/operator-controller/internal/applier" "github.com/operator-framework/operator-controller/internal/authentication" @@ -141,8 +141,8 @@ func main() { setupLog.Info("set up manager") cacheOptions := crcache.Options{ ByObject: map[client.Object]crcache.ByObject{ - &ocv1alpha1.ClusterExtension{}: {Label: k8slabels.Everything()}, - &catalogd.ClusterCatalog{}: {Label: k8slabels.Everything()}, + &ocv1.ClusterExtension{}: {Label: k8slabels.Everything()}, + &catalogd.ClusterCatalog{}: {Label: k8slabels.Everything()}, }, DefaultNamespaces: map[string]crcache.Config{ systemNamespace: {LabelSelector: k8slabels.Everything()}, @@ -196,7 +196,7 @@ func main() { cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(), helmclient.StorageDriverMapper(action.ChunkedStorageDriverMapper(coreClient, mgr.GetAPIReader(), systemNamespace)), helmclient.ClientNamespaceMapper(func(obj client.Object) (string, error) { - ext := obj.(*ocv1alpha1.ClusterExtension) + ext := obj.(*ocv1.ClusterExtension) return ext.Spec.Namespace, nil }), helmclient.ClientRestConfigMapper(clientRestConfigMapper), @@ -291,7 +291,7 @@ func main() { cm := contentmanager.NewManager(clientRestConfigMapper, mgr.GetConfig(), mgr.GetRESTMapper()) err = clusterExtensionFinalizers.Register(controllers.ClusterExtensionCleanupContentManagerCacheFinalizer, finalizers.FinalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) { - ext := obj.(*ocv1alpha1.ClusterExtension) + ext := obj.(*ocv1.ClusterExtension) err := cm.Delete(ext) return crfinalizer.Result{}, err })) diff --git a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml index ef946c56a4..a908b256d5 100644 --- a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml +++ b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -30,7 +30,7 @@ spec: - jsonPath: .metadata.creationTimestamp name: Age type: date - name: v1alpha1 + name: v1 schema: openAPIV3Schema: description: ClusterExtension is the Schema for the clusterextensions API diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index bd17831769..7816e8fefb 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,5 +1,5 @@ ## Append samples of your project ## resources: -- olm_v1alpha1_clusterextension.yaml +- olm_v1_clusterextension.yaml - olm_v1alpha1_extension.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/olm_v1alpha1_clusterextension.yaml b/config/samples/olm_v1_clusterextension.yaml similarity index 99% rename from config/samples/olm_v1alpha1_clusterextension.yaml rename to config/samples/olm_v1_clusterextension.yaml index f9bc5ff10f..80aa801ae7 100644 --- a/config/samples/olm_v1alpha1_clusterextension.yaml +++ b/config/samples/olm_v1_clusterextension.yaml @@ -267,7 +267,7 @@ subjects: name: argocd-installer namespace: argocd --- -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd diff --git a/docs/api-reference/operator-controller-api-reference.md b/docs/api-reference/operator-controller-api-reference.md index 8977d3a3a6..63ea96df6f 100644 --- a/docs/api-reference/operator-controller-api-reference.md +++ b/docs/api-reference/operator-controller-api-reference.md @@ -1,10 +1,10 @@ # API Reference ## Packages -- [olm.operatorframework.io/v1alpha1](#olmoperatorframeworkiov1alpha1) +- [olm.operatorframework.io/v1](#olmoperatorframeworkiov1) -## olm.operatorframework.io/v1alpha1 +## olm.operatorframework.io/v1 Package v1alpha1 contains API Schema definitions for the olm v1alpha1 API group @@ -97,7 +97,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `apiVersion` _string_ | `olm.operatorframework.io/v1alpha1` | | | +| `apiVersion` _string_ | `olm.operatorframework.io/v1` | | | | `kind` _string_ | `ClusterExtension` | | | | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | @@ -151,7 +151,7 @@ ClusterExtensionList contains a list of ClusterExtension | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `apiVersion` _string_ | `olm.operatorframework.io/v1alpha1` | | | +| `apiVersion` _string_ | `olm.operatorframework.io/v1` | | | | `kind` _string_ | `ClusterExtensionList` | | | | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | diff --git a/docs/concepts/controlling-catalog-selection.md b/docs/concepts/controlling-catalog-selection.md index 13b782d0fe..ec987e5567 100644 --- a/docs/concepts/controlling-catalog-selection.md +++ b/docs/concepts/controlling-catalog-selection.md @@ -18,7 +18,7 @@ To select a specific catalog by name, you can use the `matchLabels` field in you #### Example ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: my-extension @@ -39,7 +39,7 @@ If you have catalogs labeled with specific metadata, you can select them using ` #### Using `matchLabels` ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: my-extension @@ -56,7 +56,7 @@ This selects catalogs labeled with `example.com/support: "true"`. #### Using `matchExpressions` ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: my-extension @@ -81,7 +81,7 @@ You can exclude catalogs by using the `NotIn` or `DoesNotExist` operators in `ma #### Example: Exclude Specific Catalogs ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: my-extension @@ -101,7 +101,7 @@ This excludes the catalog named `unwanted-catalog` from consideration. #### Example: Exclude Catalogs with a Specific Label ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: my-extension @@ -194,7 +194,7 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera 2. **Create a `ClusterExtension` with Catalog Selection** ```yaml - apiVersion: olm.operatorframework.io/v1alpha1 + apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: install-my-operator diff --git a/docs/concepts/crd-upgrade-safety.md b/docs/concepts/crd-upgrade-safety.md index 47ad18d7ba..339315472b 100644 --- a/docs/concepts/crd-upgrade-safety.md +++ b/docs/concepts/crd-upgrade-safety.md @@ -56,7 +56,7 @@ The CRD Upgrade Safety preflight check can be entirely disabled by adding the `preflight.crdUpgradeSafety.disabled` field with a value of "true" to the ClusterExtension of the CRD. ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: clusterextension-sample diff --git a/docs/concepts/upgrade-support.md b/docs/concepts/upgrade-support.md index 9bc6e31adf..5abd579f1b 100644 --- a/docs/concepts/upgrade-support.md +++ b/docs/concepts/upgrade-support.md @@ -38,7 +38,7 @@ If `1.0.0` is installed, OLM v1 behavior differs in the following ways: You can change the default behavior of the upgrade constraints by setting the `upgradeConstraintPolicy` parameter in your cluster extension's custom resource (CR). ``` yaml hl_lines="10" -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: @@ -86,7 +86,7 @@ If you set the field to `SelfCertified`, no upgrade constraints are set on the p Example `ClusterExtension` with `.spec.upgradeConstraintPolicy` field set to `SelfCertified`: ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: extension-sample diff --git a/docs/howto/how-to-channel-based-upgrades.md b/docs/howto/how-to-channel-based-upgrades.md index 501a7f9519..e7638d1a19 100644 --- a/docs/howto/how-to-channel-based-upgrades.md +++ b/docs/howto/how-to-channel-based-upgrades.md @@ -5,7 +5,7 @@ A "channel" is a package author defined stream of updates for an extension. A se Example: ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd @@ -27,7 +27,7 @@ Note that the `version` field also accepts a version range to further restrict t Example: ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd diff --git a/docs/howto/how-to-pin-version.md b/docs/howto/how-to-pin-version.md index 606b994aad..5dc0660b0b 100644 --- a/docs/howto/how-to-pin-version.md +++ b/docs/howto/how-to-pin-version.md @@ -5,7 +5,7 @@ To disable automatic updates, and pin the version of an extension, set `version` Example: ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd diff --git a/docs/howto/how-to-version-range-upgrades.md b/docs/howto/how-to-version-range-upgrades.md index ddb753fbad..dc239fa39a 100644 --- a/docs/howto/how-to-version-range-upgrades.md +++ b/docs/howto/how-to-version-range-upgrades.md @@ -5,7 +5,7 @@ Set the version for the desired package in the Catalog source to a comparison st Example: ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd diff --git a/docs/howto/how-to-z-stream-upgrades.md b/docs/howto/how-to-z-stream-upgrades.md index 8666e09b74..1a638fd1b3 100644 --- a/docs/howto/how-to-z-stream-upgrades.md +++ b/docs/howto/how-to-z-stream-upgrades.md @@ -5,7 +5,7 @@ To restrict automatic updates to only z-stream patches and avoid breaking change Example: ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd diff --git a/docs/tutorials/downgrade-extension.md b/docs/tutorials/downgrade-extension.md index 0e57d46873..e400600faf 100644 --- a/docs/tutorials/downgrade-extension.md +++ b/docs/tutorials/downgrade-extension.md @@ -31,7 +31,7 @@ Add the `crdUpgradeSafety` field and set its `policy` to `Disabled` in the `Clus **Example:** ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: example-extension @@ -71,7 +71,7 @@ Set the `upgradeConstraintPolicy` to `SelfCertified` in the `ClusterExtension` r **Example:** ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: example-extension @@ -113,7 +113,7 @@ Once the CRD safety checks are disabled and upgrade constraints are set, you can Within the YAML editor, update the `spec` section as follows: ```yaml - apiVersion: olm.operatorframework.io/v1alpha1 + apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: diff --git a/docs/tutorials/install-extension.md b/docs/tutorials/install-extension.md index 95bdb5c3ab..92b7aadeb1 100644 --- a/docs/tutorials/install-extension.md +++ b/docs/tutorials/install-extension.md @@ -34,7 +34,7 @@ For information on determining the ServiceAccount's permission, please see [Deri 1. Create a CR for the Kubernetes extension you want to install: ``` yaml title="Example CR" - apiVersion: olm.operatorframework.io/v1alpha1 + apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: @@ -107,7 +107,7 @@ For information on determining the ServiceAccount's permission, please see [Deri Namespace: Labels: Annotations: - API Version: olm.operatorframework.io/v1alpha1 + API Version: olm.operatorframework.io/v1 Kind: ClusterExtension Metadata: Creation Timestamp: 2024-10-03T16:02:40Z diff --git a/docs/tutorials/upgrade-extension.md b/docs/tutorials/upgrade-extension.md index ea0a203441..1c0e8b0616 100644 --- a/docs/tutorials/upgrade-extension.md +++ b/docs/tutorials/upgrade-extension.md @@ -23,7 +23,7 @@ For more detailed information see [Upgrade Support](../concepts/upgrade-support. Suppose we have successfully created and installed v0.5.0 of the ArgoCD operator with the following `ClusterExtension`: ``` yaml title="Example CR" -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd @@ -43,7 +43,7 @@ spec: ``` terminal kubectl apply -f - < 0 { - status, reason, message = metav1.ConditionTrue, ocv1alpha1.ReasonDeprecated, strings.Join(deprecationMessages, ";") + status, reason, message = metav1.ConditionTrue, ocv1.ReasonDeprecated, strings.Join(deprecationMessages, ";") } apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeDeprecated, + Type: ocv1.TypeDeprecated, Reason: reason, Status: status, Message: message, @@ -383,14 +383,14 @@ func SetDeprecationStatus(ext *ocv1alpha1.ClusterExtension, bundleName string, d // finally, set the individual deprecation conditions for package, channel, and bundle for _, conditionType := range []string{ - ocv1alpha1.TypePackageDeprecated, - ocv1alpha1.TypeChannelDeprecated, - ocv1alpha1.TypeBundleDeprecated, + ocv1.TypePackageDeprecated, + ocv1.TypeChannelDeprecated, + ocv1.TypeBundleDeprecated, } { entries, ok := deprecations[conditionType] - status, reason, message := metav1.ConditionFalse, ocv1alpha1.ReasonDeprecated, "" + status, reason, message := metav1.ConditionFalse, ocv1.ReasonDeprecated, "" if ok { - status, reason = metav1.ConditionTrue, ocv1alpha1.ReasonDeprecated + status, reason = metav1.ConditionTrue, ocv1.ReasonDeprecated for _, entry := range entries { message = fmt.Sprintf("%s\n%s", message, entry.Message) } @@ -408,7 +408,7 @@ func SetDeprecationStatus(ext *ocv1alpha1.ClusterExtension, bundleName string, d // SetupWithManager sets up the controller with the Manager. func (r *ClusterExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error { controller, err := ctrl.NewControllerManagedBy(mgr). - For(&ocv1alpha1.ClusterExtension{}). + For(&ocv1.ClusterExtension{}). Watches(&catalogd.ClusterCatalog{}, crhandler.EnqueueRequestsFromMapFunc(clusterExtensionRequestsForCatalog(mgr.GetClient(), mgr.GetLogger())), builder.WithPredicates(predicate.Funcs{ @@ -438,7 +438,7 @@ func (r *ClusterExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error { return nil } -func wrapErrorWithResolutionInfo(resolved ocv1alpha1.BundleMetadata, err error) error { +func wrapErrorWithResolutionInfo(resolved ocv1.BundleMetadata, err error) error { return fmt.Errorf("%w for resolved bundle %q with version %q", err, resolved.Name, resolved.Version) } @@ -447,7 +447,7 @@ func clusterExtensionRequestsForCatalog(c client.Reader, logger logr.Logger) crh return func(ctx context.Context, _ client.Object) []reconcile.Request { // no way of associating an extension to a catalog so create reconcile requests for everything clusterExtensions := metav1.PartialObjectMetadataList{} - clusterExtensions.SetGroupVersionKind(ocv1alpha1.GroupVersion.WithKind("ClusterExtensionList")) + clusterExtensions.SetGroupVersionKind(ocv1.GroupVersion.WithKind("ClusterExtensionList")) err := c.List(ctx, &clusterExtensions) if err != nil { logger.Error(err, "unable to enqueue cluster extensions for catalog reconcile") @@ -471,11 +471,11 @@ type DefaultInstalledBundleGetter struct { } type InstalledBundle struct { - ocv1alpha1.BundleMetadata + ocv1.BundleMetadata Image string } -func (d *DefaultInstalledBundleGetter) GetInstalledBundle(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (*InstalledBundle, error) { +func (d *DefaultInstalledBundleGetter) GetInstalledBundle(ctx context.Context, ext *ocv1.ClusterExtension) (*InstalledBundle, error) { cl, err := d.ActionClientFor(ctx, ext) if err != nil { return nil, err @@ -494,7 +494,7 @@ func (d *DefaultInstalledBundleGetter) GetInstalledBundle(ctx context.Context, e for _, rel := range relhis { if rel.Info != nil && rel.Info.Status == release.StatusDeployed { return &InstalledBundle{ - BundleMetadata: ocv1alpha1.BundleMetadata{ + BundleMetadata: ocv1.BundleMetadata{ Name: rel.Labels[labels.BundleNameKey], Version: rel.Labels[labels.BundleVersionKey], }, diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index 448327944b..ab4dc5e184 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -27,7 +27,7 @@ import ( helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" "github.com/operator-framework/operator-registry/alpha/declcfg" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/conditionsets" "github.com/operator-framework/operator-controller/internal/controllers" "github.com/operator-framework/operator-controller/internal/finalizers" @@ -50,7 +50,7 @@ func TestClusterExtensionDoesNotExist(t *testing.T) { func TestClusterExtensionResolutionFails(t *testing.T) { pkgName := fmt.Sprintf("non-existent-%s", rand.String(6)) cl, reconciler := newClientAndReconciler(t) - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { return nil, nil, nil, fmt.Errorf("no package %q found", pkgName) }) ctx := context.Background() @@ -58,17 +58,17 @@ func TestClusterExtensionResolutionFails(t *testing.T) { t.Log("When the cluster extension specifies a non-existent package") t.Log("By initializing cluster state") - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkgName, }, }, Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: "default", }, }, @@ -88,14 +88,14 @@ func TestClusterExtensionResolutionFails(t *testing.T) { require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected conditions") - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, ocv1alpha1.ReasonRetrying, cond.Reason) + require.Equal(t, ocv1.ReasonRetrying, cond.Reason) require.Equal(t, fmt.Sprintf("no package %q found", pkgName), cond.Message) verifyInvariants(ctx, t, reconciler.Client, clusterExtension) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1.ClusterExtension{})) } func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { @@ -132,19 +132,19 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkgName, Version: pkgVer, Channels: []string{pkgChan}, }, }, Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: serviceAccount, }, }, @@ -154,7 +154,7 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { t.Log("It sets resolution success status") t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { v := bsemver.MustParse("1.0.0") return &declcfg.Bundle{ Name: "prometheus.v1.0.0", @@ -174,23 +174,23 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} + expectedBundleMetadata := ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected conditions") expectStatus := metav1.ConditionTrue - expectReason := ocv1alpha1.ReasonRetrying + expectReason := ocv1.ReasonRetrying if tc.expectTerminal { expectStatus = metav1.ConditionFalse - expectReason = ocv1alpha1.ReasonBlocked + expectReason = ocv1.ReasonBlocked } - progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, progressingCond) require.Equal(t, expectStatus, progressingCond.Status) require.Equal(t, expectReason, progressingCond.Reason) require.Contains(t, progressingCond.Message, fmt.Sprintf("for resolved bundle %q with version %q", expectedBundleMetadata.Name, expectedBundleMetadata.Version)) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1.ClusterExtension{})) }) } } @@ -214,19 +214,19 @@ func TestClusterExtensionUnpackUnexpectedState(t *testing.T) { namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkgName, Version: pkgVer, Channels: []string{pkgChan}, }, }, Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: serviceAccount, }, }, @@ -236,7 +236,7 @@ func TestClusterExtensionUnpackUnexpectedState(t *testing.T) { t.Log("It sets resolution success status") t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { v := bsemver.MustParse("1.0.0") return &declcfg.Bundle{ Name: "prometheus.v1.0.0", @@ -249,7 +249,7 @@ func TestClusterExtensionUnpackUnexpectedState(t *testing.T) { _, _ = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) }, "reconciliation should panic on unknown unpack state") - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1.ClusterExtension{})) } func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) { @@ -272,19 +272,19 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkgName, Version: pkgVer, Channels: []string{pkgChan}, }, }, Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: serviceAccount, }, }, @@ -294,7 +294,7 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) t.Log("It sets resolution success status") t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { v := bsemver.MustParse("1.0.0") return &declcfg.Bundle{ Name: "prometheus.v1.0.0", @@ -313,23 +313,23 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} + expectedBundleMetadata := ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} require.Empty(t, clusterExtension.Status.Install) t.Log("By checking the expected installed conditions") - installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) require.NotNil(t, installedCond) require.Equal(t, metav1.ConditionFalse, installedCond.Status) - require.Equal(t, ocv1alpha1.ReasonFailed, installedCond.Reason) + require.Equal(t, ocv1.ReasonFailed, installedCond.Reason) t.Log("By checking the expected progressing conditions") - progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, progressingCond) require.Equal(t, metav1.ConditionTrue, progressingCond.Status) - require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) + require.Equal(t, ocv1.ReasonRetrying, progressingCond.Reason) require.Contains(t, progressingCond.Message, fmt.Sprintf("for resolved bundle %q with version %q", expectedBundleMetadata.Name, expectedBundleMetadata.Version)) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1.ClusterExtension{})) } func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { @@ -352,19 +352,19 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkgName, Version: pkgVer, Channels: []string{pkgChan}, }, }, Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: serviceAccount, }, }, @@ -374,7 +374,7 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { t.Log("It sets resolution success status") t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { v := bsemver.MustParse("1.0.0") return &declcfg.Bundle{ Name: "prometheus.v1.0.0", @@ -388,7 +388,7 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { } reconciler.InstalledBundleGetter = &MockInstalledBundleGetter{ bundle: &controllers.InstalledBundle{ - BundleMetadata: ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + BundleMetadata: ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, } @@ -412,23 +412,23 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} + expectedBundleMetadata := ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Install.Bundle) t.Log("By checking the expected installed conditions") - installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) require.NotNil(t, installedCond) require.Equal(t, metav1.ConditionTrue, installedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSucceeded, installedCond.Reason) + require.Equal(t, ocv1.ReasonSucceeded, installedCond.Reason) t.Log("By checking the expected progressing conditions") - progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, progressingCond) require.Equal(t, metav1.ConditionTrue, progressingCond.Status) - require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) + require.Equal(t, ocv1.ReasonRetrying, progressingCond.Reason) require.Contains(t, progressingCond.Message, fmt.Sprintf("for resolved bundle %q with version %q", expectedBundleMetadata.Name, expectedBundleMetadata.Version)) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1.ClusterExtension{})) } func TestClusterExtensionManagerFailed(t *testing.T) { @@ -451,19 +451,19 @@ func TestClusterExtensionManagerFailed(t *testing.T) { namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkgName, Version: pkgVer, Channels: []string{pkgChan}, }, }, Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: serviceAccount, }, }, @@ -473,7 +473,7 @@ func TestClusterExtensionManagerFailed(t *testing.T) { t.Log("It sets resolution success status") t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { v := bsemver.MustParse("1.0.0") return &declcfg.Bundle{ Name: "prometheus.v1.0.0", @@ -495,21 +495,21 @@ func TestClusterExtensionManagerFailed(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) + require.Equal(t, ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) t.Log("By checking the expected installed conditions") - installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) require.NotNil(t, installedCond) require.Equal(t, metav1.ConditionTrue, installedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSucceeded, installedCond.Reason) + require.Equal(t, ocv1.ReasonSucceeded, installedCond.Reason) t.Log("By checking the expected progressing conditions") - progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, progressingCond) require.Equal(t, metav1.ConditionTrue, progressingCond.Status) - require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) + require.Equal(t, ocv1.ReasonRetrying, progressingCond.Reason) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1.ClusterExtension{})) } func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { @@ -532,20 +532,20 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8)) serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ - SourceType: ocv1alpha1.SourceTypeCatalog, + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ + SourceType: ocv1.SourceTypeCatalog, - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkgName, Version: pkgVer, Channels: []string{pkgChan}, }, }, Namespace: installNamespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: serviceAccount, }, }, @@ -555,7 +555,7 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { t.Log("It sets resolution success status") t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { v := bsemver.MustParse("1.0.0") return &declcfg.Bundle{ Name: "prometheus.v1.0.0", @@ -579,21 +579,21 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) + require.Equal(t, ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) t.Log("By checking the expected installed conditions") - installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) require.NotNil(t, installedCond) require.Equal(t, metav1.ConditionTrue, installedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSucceeded, installedCond.Reason) + require.Equal(t, ocv1.ReasonSucceeded, installedCond.Reason) t.Log("By checking the expected progressing conditions") - progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, progressingCond) require.Equal(t, metav1.ConditionTrue, progressingCond.Status) - require.Equal(t, ocv1alpha1.ReasonRetrying, progressingCond.Reason) + require.Equal(t, ocv1.ReasonRetrying, progressingCond.Reason) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1.ClusterExtension{})) } func TestClusterExtensionInstallationSucceeds(t *testing.T) { @@ -616,19 +616,19 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkgName, Version: pkgVer, Channels: []string{pkgChan}, }, }, Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: serviceAccount, }, }, @@ -638,7 +638,7 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { t.Log("It sets resolution success status") t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { v := bsemver.MustParse("1.0.0") return &declcfg.Bundle{ Name: "prometheus.v1.0.0", @@ -660,21 +660,21 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) t.Log("By checking the status fields") - require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) + require.Equal(t, ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle) t.Log("By checking the expected installed conditions") - installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + installedCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) require.NotNil(t, installedCond) require.Equal(t, metav1.ConditionTrue, installedCond.Status) - require.Equal(t, ocv1alpha1.ReasonSucceeded, installedCond.Reason) + require.Equal(t, ocv1.ReasonSucceeded, installedCond.Reason) t.Log("By checking the expected progressing conditions") - progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, progressingCond) require.Equal(t, metav1.ConditionTrue, progressingCond.Status) - require.Equal(t, ocv1alpha1.ReasonSucceeded, progressingCond.Reason) + require.Equal(t, ocv1.ReasonSucceeded, progressingCond.Reason) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1.ClusterExtension{})) } func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { @@ -697,19 +697,19 @@ func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { namespace := fmt.Sprintf("test-ns-%s", rand.String(8)) serviceAccount := fmt.Sprintf("test-sa-%s", rand.String(8)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkgName, Version: pkgVer, Channels: []string{pkgChan}, }, }, Namespace: namespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: serviceAccount, }, }, @@ -718,7 +718,7 @@ func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { require.NoError(t, err) t.Log("It sets resolution success status") t.Log("By running reconcile") - reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { + reconciler.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { v := bsemver.MustParse("1.0.0") return &declcfg.Bundle{ Name: "prometheus.v1.0.0", @@ -736,7 +736,7 @@ func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { } reconciler.InstalledBundleGetter = &MockInstalledBundleGetter{ bundle: &controllers.InstalledBundle{ - BundleMetadata: ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + BundleMetadata: ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, } @@ -756,37 +756,37 @@ func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { t.Log("By fetching updated cluster extension after first reconcile") require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) - expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) + expectedBundleMetadata := ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"} require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Install.Bundle) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) + require.NoError(t, cl.DeleteAllOf(ctx, &ocv1.ClusterExtension{})) res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Error(t, err, res) t.Log("By fetching updated cluster extension after second reconcile") require.NoError(t, cl.Get(ctx, extKey, clusterExtension)) - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Install.Bundle) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) require.Equal(t, fakeFinalizer, clusterExtension.Finalizers[0]) - cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) require.Contains(t, cond.Message, finalizersMessage) } -func verifyInvariants(ctx context.Context, t *testing.T, c client.Client, ext *ocv1alpha1.ClusterExtension) { +func verifyInvariants(ctx context.Context, t *testing.T, c client.Client, ext *ocv1.ClusterExtension) { key := client.ObjectKeyFromObject(ext) require.NoError(t, c.Get(ctx, key, ext)) verifyConditionsInvariants(t, ext) } -func verifyConditionsInvariants(t *testing.T, ext *ocv1alpha1.ClusterExtension) { +func verifyConditionsInvariants(t *testing.T, ext *ocv1.ClusterExtension) { // Expect that the cluster extension's set of conditions contains all defined // condition types for the ClusterExtension API. Every reconcile should always // ensure every condition type's status/reason/message reflects the state @@ -804,48 +804,48 @@ func verifyConditionsInvariants(t *testing.T, ext *ocv1alpha1.ClusterExtension) func TestSetDeprecationStatus(t *testing.T) { for _, tc := range []struct { name string - clusterExtension *ocv1alpha1.ClusterExtension - expectedClusterExtension *ocv1alpha1.ClusterExtension + clusterExtension *ocv1.ClusterExtension + expectedClusterExtension *ocv1.ClusterExtension bundle *declcfg.Bundle deprecation *declcfg.Deprecation }{ { name: "no deprecations, all deprecation statuses set to False", - clusterExtension: &ocv1alpha1.ClusterExtension{ + clusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{}, }, }, - expectedClusterExtension: &ocv1alpha1.ClusterExtension{ + expectedClusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{ { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypePackageDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypePackageDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeChannelDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeChannelDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeBundleDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeBundleDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, @@ -857,53 +857,53 @@ func TestSetDeprecationStatus(t *testing.T) { }, { name: "deprecated channel, but no channel specified, all deprecation statuses set to False", - clusterExtension: &ocv1alpha1.ClusterExtension{ + clusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{}, + Catalog: &ocv1.CatalogSource{}, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{}, }, }, - expectedClusterExtension: &ocv1alpha1.ClusterExtension{ + expectedClusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{}, + Catalog: &ocv1.CatalogSource{}, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{ { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypePackageDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypePackageDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeChannelDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeChannelDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeBundleDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeBundleDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, @@ -922,57 +922,57 @@ func TestSetDeprecationStatus(t *testing.T) { }, { name: "deprecated channel, but a non-deprecated channel specified, all deprecation statuses set to False", - clusterExtension: &ocv1alpha1.ClusterExtension{ + clusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"nondeprecated"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{}, }, }, - expectedClusterExtension: &ocv1alpha1.ClusterExtension{ + expectedClusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"nondeprecated"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{ { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypePackageDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypePackageDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeChannelDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeChannelDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeBundleDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeBundleDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, @@ -993,57 +993,57 @@ func TestSetDeprecationStatus(t *testing.T) { }, { name: "deprecated channel specified, ChannelDeprecated and Deprecated status set to true, others set to false", - clusterExtension: &ocv1alpha1.ClusterExtension{ + clusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{}, }, }, - expectedClusterExtension: &ocv1alpha1.ClusterExtension{ + expectedClusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{ { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypePackageDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypePackageDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeChannelDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeChannelDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeBundleDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeBundleDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, @@ -1065,57 +1065,57 @@ func TestSetDeprecationStatus(t *testing.T) { }, { name: "deprecated package and channel specified, deprecated bundle, all deprecation statuses set to true", - clusterExtension: &ocv1alpha1.ClusterExtension{ + clusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{}, }, }, - expectedClusterExtension: &ocv1alpha1.ClusterExtension{ + expectedClusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{ { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypePackageDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypePackageDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeChannelDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeChannelDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeBundleDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeBundleDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, @@ -1150,57 +1150,57 @@ func TestSetDeprecationStatus(t *testing.T) { }, { name: "deprecated channel specified, deprecated bundle, all deprecation statuses set to true, all deprecation statuses set to true except PackageDeprecated", - clusterExtension: &ocv1alpha1.ClusterExtension{ + clusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{}, }, }, - expectedClusterExtension: &ocv1alpha1.ClusterExtension{ + expectedClusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{ { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypePackageDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypePackageDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeChannelDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeChannelDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeBundleDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeBundleDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, @@ -1229,57 +1229,57 @@ func TestSetDeprecationStatus(t *testing.T) { }, { name: "deprecated package and channel specified, all deprecation statuses set to true except BundleDeprecated", - clusterExtension: &ocv1alpha1.ClusterExtension{ + clusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{}, }, }, - expectedClusterExtension: &ocv1alpha1.ClusterExtension{ + expectedClusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{ { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypePackageDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypePackageDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeChannelDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeChannelDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeBundleDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeBundleDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, @@ -1307,57 +1307,57 @@ func TestSetDeprecationStatus(t *testing.T) { }, { name: "deprecated channels specified, ChannelDeprecated and Deprecated status set to true, others set to false", - clusterExtension: &ocv1alpha1.ClusterExtension{ + clusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel", "anotherbadchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{}, }, }, - expectedClusterExtension: &ocv1alpha1.ClusterExtension{ + expectedClusterExtension: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Generation: 1, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ Channels: []string{"badchannel", "anotherbadchannel"}, }, }, }, - Status: ocv1alpha1.ClusterExtensionStatus{ + Status: ocv1.ClusterExtensionStatus{ Conditions: []metav1.Condition{ { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypePackageDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypePackageDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeChannelDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeChannelDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionTrue, ObservedGeneration: 1, }, { - Type: ocv1alpha1.TypeBundleDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, + Type: ocv1.TypeBundleDeprecated, + Reason: ocv1.ReasonDeprecated, Status: metav1.ConditionFalse, ObservedGeneration: 1, }, @@ -1434,7 +1434,7 @@ func (mag *MockActionGetter) Reconcile(rel *release.Release) error { func TestGetInstalledBundleHistory(t *testing.T) { getter := controllers.DefaultInstalledBundleGetter{} - ext := ocv1alpha1.ClusterExtension{ + ext := ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "test-ext", }, @@ -1473,7 +1473,7 @@ func TestGetInstalledBundleHistory(t *testing.T) { }, nil, &controllers.InstalledBundle{ - BundleMetadata: ocv1alpha1.BundleMetadata{ + BundleMetadata: ocv1.BundleMetadata{ Name: "test-ext", Version: "1.0", }, @@ -1508,7 +1508,7 @@ func TestGetInstalledBundleHistory(t *testing.T) { }, nil, &controllers.InstalledBundle{ - BundleMetadata: ocv1alpha1.BundleMetadata{ + BundleMetadata: ocv1.BundleMetadata{ Name: "test-ext", Version: "1.0", }, diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index 1da2d8a136..7cee10c100 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -24,11 +24,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/reconcile" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" ) // setInstalledStatusFromBundle sets the installed status based on the given installedBundle. -func setInstalledStatusFromBundle(ext *ocv1alpha1.ClusterExtension, installedBundle *InstalledBundle) { +func setInstalledStatusFromBundle(ext *ocv1.ClusterExtension, installedBundle *InstalledBundle) { // Nothing is installed if installedBundle == nil { setInstallStatus(ext, nil) @@ -36,7 +36,7 @@ func setInstalledStatusFromBundle(ext *ocv1alpha1.ClusterExtension, installedBun return } // Something is installed - installStatus := &ocv1alpha1.ClusterExtensionInstallStatus{ + installStatus := &ocv1.ClusterExtensionInstallStatus{ Bundle: installedBundle.BundleMetadata, } setInstallStatus(ext, installStatus) @@ -44,59 +44,59 @@ func setInstalledStatusFromBundle(ext *ocv1alpha1.ClusterExtension, installedBun } // setInstalledStatusConditionSuccess sets the installed status condition to success. -func setInstalledStatusConditionSuccess(ext *ocv1alpha1.ClusterExtension, message string) { +func setInstalledStatusConditionSuccess(ext *ocv1.ClusterExtension, message string) { apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeInstalled, + Type: ocv1.TypeInstalled, Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonSucceeded, + Reason: ocv1.ReasonSucceeded, Message: message, ObservedGeneration: ext.GetGeneration(), }) } // setInstalledStatusConditionFailed sets the installed status condition to failed. -func setInstalledStatusConditionFailed(ext *ocv1alpha1.ClusterExtension, message string) { +func setInstalledStatusConditionFailed(ext *ocv1.ClusterExtension, message string) { apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeInstalled, + Type: ocv1.TypeInstalled, Status: metav1.ConditionFalse, - Reason: ocv1alpha1.ReasonFailed, + Reason: ocv1.ReasonFailed, Message: message, ObservedGeneration: ext.GetGeneration(), }) } // setInstalledStatusConditionUnknown sets the installed status condition to unknown. -func setInstalledStatusConditionUnknown(ext *ocv1alpha1.ClusterExtension, message string) { +func setInstalledStatusConditionUnknown(ext *ocv1.ClusterExtension, message string) { apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{ - Type: ocv1alpha1.TypeInstalled, + Type: ocv1.TypeInstalled, Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonFailed, + Reason: ocv1.ReasonFailed, Message: message, ObservedGeneration: ext.GetGeneration(), }) } -func setInstallStatus(ext *ocv1alpha1.ClusterExtension, installStatus *ocv1alpha1.ClusterExtensionInstallStatus) { +func setInstallStatus(ext *ocv1.ClusterExtension, installStatus *ocv1.ClusterExtensionInstallStatus) { ext.Status.Install = installStatus } -func setStatusProgressing(ext *ocv1alpha1.ClusterExtension, err error) { +func setStatusProgressing(ext *ocv1.ClusterExtension, err error) { progressingCond := metav1.Condition{ - Type: ocv1alpha1.TypeProgressing, + Type: ocv1.TypeProgressing, Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonSucceeded, + Reason: ocv1.ReasonSucceeded, Message: "desired state reached", ObservedGeneration: ext.GetGeneration(), } if err != nil { - progressingCond.Reason = ocv1alpha1.ReasonRetrying + progressingCond.Reason = ocv1.ReasonRetrying progressingCond.Message = err.Error() } if errors.Is(err, reconcile.TerminalError(nil)) { progressingCond.Status = metav1.ConditionFalse - progressingCond.Reason = ocv1alpha1.ReasonBlocked + progressingCond.Reason = ocv1.ReasonBlocked } apimeta.SetStatusCondition(&ext.Status.Conditions, progressingCond) diff --git a/internal/controllers/common_controller_test.go b/internal/controllers/common_controller_test.go index 40aacce725..7b644172d1 100644 --- a/internal/controllers/common_controller_test.go +++ b/internal/controllers/common_controller_test.go @@ -11,53 +11,53 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/reconcile" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" ) func TestSetStatusProgressing(t *testing.T) { for _, tc := range []struct { name string err error - clusterExtension *ocv1alpha1.ClusterExtension + clusterExtension *ocv1.ClusterExtension expected metav1.Condition }{ { name: "non-nil ClusterExtension, nil error, Progressing condition has status True with reason Success", err: nil, - clusterExtension: &ocv1alpha1.ClusterExtension{}, + clusterExtension: &ocv1.ClusterExtension{}, expected: metav1.Condition{ - Type: ocv1alpha1.TypeProgressing, + Type: ocv1.TypeProgressing, Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonSucceeded, + Reason: ocv1.ReasonSucceeded, Message: "desired state reached", }, }, { name: "non-nil ClusterExtension, non-terminal error, Progressing condition has status True with reason Retrying", err: errors.New("boom"), - clusterExtension: &ocv1alpha1.ClusterExtension{}, + clusterExtension: &ocv1.ClusterExtension{}, expected: metav1.Condition{ - Type: ocv1alpha1.TypeProgressing, + Type: ocv1.TypeProgressing, Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonRetrying, + Reason: ocv1.ReasonRetrying, Message: "boom", }, }, { name: "non-nil ClusterExtension, terminal error, Progressing condition has status False with reason Blocked", err: reconcile.TerminalError(errors.New("boom")), - clusterExtension: &ocv1alpha1.ClusterExtension{}, + clusterExtension: &ocv1.ClusterExtension{}, expected: metav1.Condition{ - Type: ocv1alpha1.TypeProgressing, + Type: ocv1.TypeProgressing, Status: metav1.ConditionFalse, - Reason: ocv1alpha1.ReasonBlocked, + Reason: ocv1.ReasonBlocked, Message: "terminal error: boom", }, }, } { t.Run(tc.name, func(t *testing.T) { setStatusProgressing(tc.clusterExtension, tc.err) - progressingCond := meta.FindStatusCondition(tc.clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + progressingCond := meta.FindStatusCondition(tc.clusterExtension.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, progressingCond, "progressing condition should be set but was not") diff := cmp.Diff(*progressingCond, tc.expected, cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime", "ObservedGeneration")) require.Empty(t, diff, "difference between actual and expected Progressing conditions") diff --git a/internal/controllers/suite_test.go b/internal/controllers/suite_test.go index 97ea3c427d..52fd8900ac 100644 --- a/internal/controllers/suite_test.go +++ b/internal/controllers/suite_test.go @@ -35,7 +35,7 @@ import ( helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/contentmanager" cmcache "github.com/operator-framework/operator-controller/internal/contentmanager/cache" "github.com/operator-framework/operator-controller/internal/controllers" @@ -65,7 +65,7 @@ func newClient(t *testing.T) client.Client { // TODO: this is a live client, which behaves differently than a cache client. // We may want to use a caching client instead to get closer to real behavior. sch := runtime.NewScheme() - require.NoError(t, ocv1alpha1.AddToScheme(sch)) + require.NoError(t, ocv1.AddToScheme(sch)) cl, err := client.New(config, client.Options{Scheme: sch}) require.NoError(t, err) require.NotNil(t, cl) @@ -80,7 +80,7 @@ func (m *MockInstalledBundleGetter) SetBundle(bundle *controllers.InstalledBundl m.bundle = bundle } -func (m *MockInstalledBundleGetter) GetInstalledBundle(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (*controllers.InstalledBundle, error) { +func (m *MockInstalledBundleGetter) GetInstalledBundle(ctx context.Context, ext *ocv1.ClusterExtension) (*controllers.InstalledBundle, error) { return m.bundle, nil } @@ -92,7 +92,7 @@ type MockApplier struct { state string } -func (m *MockApplier) Apply(_ context.Context, _ fs.FS, _ *ocv1alpha1.ClusterExtension, _ map[string]string, _ map[string]string) ([]client.Object, string, error) { +func (m *MockApplier) Apply(_ context.Context, _ fs.FS, _ *ocv1.ClusterExtension, _ map[string]string, _ map[string]string) ([]client.Object, string, error) { if m.err != nil { return nil, m.state, m.err } @@ -107,14 +107,14 @@ type MockManagedContentCacheManager struct { cache cmcache.Cache } -func (m *MockManagedContentCacheManager) Get(_ context.Context, _ *ocv1alpha1.ClusterExtension) (cmcache.Cache, error) { +func (m *MockManagedContentCacheManager) Get(_ context.Context, _ *ocv1.ClusterExtension) (cmcache.Cache, error) { if m.err != nil { return nil, m.err } return m.cache, nil } -func (m *MockManagedContentCacheManager) Delete(_ *ocv1alpha1.ClusterExtension) error { +func (m *MockManagedContentCacheManager) Delete(_ *ocv1.ClusterExtension) error { return m.err } diff --git a/internal/resolve/catalog.go b/internal/resolve/catalog.go index 761448bea5..8597710556 100644 --- a/internal/resolve/catalog.go +++ b/internal/resolve/catalog.go @@ -18,7 +18,7 @@ import ( catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" "github.com/operator-framework/operator-registry/alpha/declcfg" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/bundleutil" "github.com/operator-framework/operator-controller/internal/catalogmetadata/compare" "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter" @@ -38,7 +38,7 @@ type foundBundle struct { } // Resolve returns a Bundle from a catalog that needs to get installed on the cluster. -func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1alpha1.ClusterExtension, installedBundle *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { +func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { packageName := ext.Spec.Source.Catalog.PackageName versionRange := ext.Spec.Source.Catalog.Version channels := ext.Spec.Source.Catalog.Channels @@ -89,7 +89,7 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1alpha1.ClusterEx predicates = append(predicates, filter.InMastermindsSemverRange(versionRangeConstraints)) } - if ext.Spec.Source.Catalog.UpgradeConstraintPolicy != ocv1alpha1.UpgradeConstraintPolicySelfCertified && installedBundle != nil { + if ext.Spec.Source.Catalog.UpgradeConstraintPolicy != ocv1.UpgradeConstraintPolicySelfCertified && installedBundle != nil { successorPredicate, err := filter.SuccessorsOf(*installedBundle, packageFBC.Channels...) if err != nil { return fmt.Errorf("error finding upgrade edges: %w", err) @@ -187,7 +187,7 @@ type resolutionError struct { PackageName string Version string Channels []string - InstalledBundle *ocv1alpha1.BundleMetadata + InstalledBundle *ocv1.BundleMetadata ResolvedBundles []foundBundle } diff --git a/internal/resolve/catalog_test.go b/internal/resolve/catalog_test.go index ba6a70027f..b59733e70f 100644 --- a/internal/resolve/catalog_test.go +++ b/internal/resolve/catalog_test.go @@ -20,14 +20,14 @@ import ( "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/features" ) func TestInvalidClusterExtensionVersionRange(t *testing.T) { r := CatalogResolver{} pkgName := randPkg() - ce := buildFooClusterExtension(pkgName, []string{}, "foobar", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "foobar", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) assert.EqualError(t, err, `desired version range "foobar" is invalid: improper constraint: foobar`) } @@ -37,7 +37,7 @@ func TestErrorWalkingCatalogs(t *testing.T) { return fmt.Errorf("fake error") }} pkgName := randPkg() - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) assert.EqualError(t, err, "error walking catalogs: fake error") } @@ -50,7 +50,7 @@ func TestErrorGettingPackage(t *testing.T) { } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} pkgName := randPkg() - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) assert.EqualError(t, err, fmt.Sprintf(`error walking catalogs: error getting package %q from catalog "a": fake error`, pkgName)) } @@ -69,7 +69,7 @@ func TestPackageDoesNotExist(t *testing.T) { } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} pkgName := randPkg() - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) assert.EqualError(t, err, fmt.Sprintf(`no bundles found for package %q`, pkgName)) } @@ -88,7 +88,7 @@ func TestPackageExists(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) assert.Equal(t, genBundle(pkgName, "3.0.0"), *gotBundle) @@ -117,7 +117,7 @@ func TestValidationFailed(t *testing.T) { }, }, } - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) } @@ -136,7 +136,7 @@ func TestVersionDoesNotExist(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "4.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "4.0.0", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) assert.EqualError(t, err, fmt.Sprintf(`no bundles found for package %q matching version "4.0.0"`, pkgName)) } @@ -155,7 +155,7 @@ func TestVersionExists(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <2.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <2.0.0", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) assert.Equal(t, genBundle(pkgName, "1.0.2"), *gotBundle) @@ -177,7 +177,7 @@ func TestChannelDoesNotExist(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{"stable"}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{"stable"}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) assert.EqualError(t, err, fmt.Sprintf(`no bundles found for package %q in channels [stable]`, pkgName)) } @@ -196,7 +196,7 @@ func TestChannelExists(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{"beta"}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{"beta"}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) assert.Equal(t, genBundle(pkgName, "1.0.2"), *gotBundle) @@ -218,7 +218,7 @@ func TestChannelExistsButNotVersion(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{"beta"}, "3.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{"beta"}, "3.0.0", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) assert.EqualError(t, err, fmt.Sprintf(`no bundles found for package %q matching version "3.0.0" in channels [beta]`, pkgName)) } @@ -237,7 +237,7 @@ func TestVersionExistsButNotChannel(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{"stable"}, "1.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{"stable"}, "1.0.0", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) assert.EqualError(t, err, fmt.Sprintf(`no bundles found for package %q matching version "1.0.0" in channels [stable]`, pkgName)) } @@ -256,7 +256,7 @@ func TestChannelAndVersionExist(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{"alpha"}, "0.1.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{"alpha"}, "0.1.0", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) assert.Equal(t, genBundle(pkgName, "0.1.0"), *gotBundle) @@ -278,7 +278,7 @@ func TestPreferNonDeprecated(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, ">=0.1.0 <=1.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=0.1.0 <=1.0.0", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) assert.Equal(t, genBundle(pkgName, "0.1.0"), *gotBundle) @@ -300,7 +300,7 @@ func TestAcceptDeprecated(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.1", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.1", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) assert.Equal(t, genBundle(pkgName, "1.0.1"), *gotBundle) @@ -383,7 +383,7 @@ func TestPackageVariationsBetweenCatalogs(t *testing.T) { r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} t.Run("when bundle candidates for a package are deprecated in all but one catalog", func(t *testing.T) { - ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.3", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.3", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) // We choose the only non-deprecated package @@ -393,7 +393,7 @@ func TestPackageVariationsBetweenCatalogs(t *testing.T) { }) t.Run("when bundle candidates are found and deprecated in multiple catalogs", func(t *testing.T) { - ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.1", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.1", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) // We will not make a decision on which catalog to use @@ -404,7 +404,7 @@ func TestPackageVariationsBetweenCatalogs(t *testing.T) { }) t.Run("when bundle candidates are found and not deprecated in multiple catalogs", func(t *testing.T) { - ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.4", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.4", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) // We will not make a decision on which catalog to use @@ -415,7 +415,7 @@ func TestPackageVariationsBetweenCatalogs(t *testing.T) { }) t.Run("highest semver bundle is chosen when candidates are all from the same catalog", func(t *testing.T) { - ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.4 <=1.0.5", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.4 <=1.0.5", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) // Bundles within one catalog for a package will be sorted by semver and deprecation and the best is returned @@ -440,8 +440,8 @@ func TestUpgradeFoundLegacy(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) - installedBundle := &ocv1alpha1.BundleMetadata{ + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) + installedBundle := &ocv1.BundleMetadata{ Name: bundleName(pkgName, "0.1.0"), Version: "0.1.0", } @@ -468,8 +468,8 @@ func TestUpgradeNotFoundLegacy(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "<1.0.0 >=2.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) - installedBundle := &ocv1alpha1.BundleMetadata{ + ce := buildFooClusterExtension(pkgName, []string{}, "<1.0.0 >=2.0.0", ocv1.UpgradeConstraintPolicyCatalogProvided) + installedBundle := &ocv1.BundleMetadata{ Name: bundleName(pkgName, "0.1.0"), Version: "0.1.0", } @@ -493,8 +493,8 @@ func TestUpgradeFoundSemver(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) - installedBundle := &ocv1alpha1.BundleMetadata{ + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) + installedBundle := &ocv1.BundleMetadata{ Name: bundleName(pkgName, "1.0.0"), Version: "1.0.0", } @@ -523,8 +523,8 @@ func TestUpgradeNotFoundSemver(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "!=0.1.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) - installedBundle := &ocv1alpha1.BundleMetadata{ + ce := buildFooClusterExtension(pkgName, []string{}, "!=0.1.0", ocv1.UpgradeConstraintPolicyCatalogProvided) + installedBundle := &ocv1.BundleMetadata{ Name: bundleName(pkgName, "0.1.0"), Version: "0.1.0", } @@ -548,8 +548,8 @@ func TestDowngradeFound(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "<1.0.2", ocv1alpha1.UpgradeConstraintPolicySelfCertified) - installedBundle := &ocv1alpha1.BundleMetadata{ + ce := buildFooClusterExtension(pkgName, []string{}, "<1.0.2", ocv1.UpgradeConstraintPolicySelfCertified) + installedBundle := &ocv1.BundleMetadata{ Name: bundleName(pkgName, "1.0.2"), Version: "1.0.2", } @@ -576,8 +576,8 @@ func TestDowngradeNotFound(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, ">0.1.0 <1.0.0", ocv1alpha1.UpgradeConstraintPolicySelfCertified) - installedBundle := &ocv1alpha1.BundleMetadata{ + ce := buildFooClusterExtension(pkgName, []string{}, ">0.1.0 <1.0.0", ocv1.UpgradeConstraintPolicySelfCertified) + installedBundle := &ocv1.BundleMetadata{ Name: bundleName(pkgName, "1.0.2"), Version: "1.0.2", } @@ -640,17 +640,17 @@ func TestCatalogWalker(t *testing.T) { }) } -func buildFooClusterExtension(pkg string, channels []string, version string, upgradeConstraintPolicy ocv1alpha1.UpgradeConstraintPolicy) *ocv1alpha1.ClusterExtension { - return &ocv1alpha1.ClusterExtension{ +func buildFooClusterExtension(pkg string, channels []string, version string, upgradeConstraintPolicy ocv1.UpgradeConstraintPolicy) *ocv1.ClusterExtension { + return &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: pkg, }, - Spec: ocv1alpha1.ClusterExtensionSpec{ + Spec: ocv1.ClusterExtensionSpec{ Namespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{Name: "default"}, - Source: ocv1alpha1.SourceConfig{ + ServiceAccount: ocv1.ServiceAccountReference{Name: "default"}, + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: pkg, Version: version, Channels: channels, @@ -760,13 +760,13 @@ func genPackage(pkg string) *declcfg.DeclarativeConfig { func TestInvalidClusterExtensionCatalogMatchExpressions(t *testing.T) { r := CatalogResolver{} - ce := &ocv1alpha1.ClusterExtension{ + ce := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ - Catalog: &ocv1alpha1.CatalogSource{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ + Catalog: &ocv1.CatalogSource{ PackageName: "foo", Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ @@ -792,13 +792,13 @@ func TestInvalidClusterExtensionCatalogMatchLabelsName(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := &ocv1alpha1.ClusterExtension{ + ce := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ - Catalog: &ocv1alpha1.CatalogSource{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ + Catalog: &ocv1.CatalogSource{ PackageName: "foo", Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"": "value"}, @@ -818,13 +818,13 @@ func TestInvalidClusterExtensionCatalogMatchLabelsValue(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := &ocv1alpha1.ClusterExtension{ + ce := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ - Catalog: &ocv1alpha1.CatalogSource{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ + Catalog: &ocv1.CatalogSource{ PackageName: "foo", Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"name": "&value"}, @@ -849,7 +849,7 @@ func TestClusterExtensionMatchLabel(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) ce.Spec.Source.Catalog.Selector = &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": "b"}, } @@ -870,7 +870,7 @@ func TestClusterExtensionNoMatchLabel(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) ce.Spec.Source.Catalog.Selector = &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": "a"}, } @@ -914,7 +914,7 @@ func TestUnequalPriority(t *testing.T) { } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) _, gotVersion, _, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) require.Equal(t, bsemver.MustParse("1.0.0"), *gotVersion) @@ -935,7 +935,7 @@ func TestMultiplePriority(t *testing.T) { } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.1", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0 <=1.0.1", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) require.ErrorContains(t, err, "in multiple catalogs with the same priority [a b c]") @@ -958,7 +958,7 @@ func TestMultipleChannels(t *testing.T) { }, } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} - ce := buildFooClusterExtension(pkgName, []string{"beta", "alpha"}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{"beta", "alpha"}, "", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, gotDeprecation, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) assert.Equal(t, genBundle(pkgName, "2.0.0"), *gotBundle) @@ -991,7 +991,7 @@ func TestAllCatalogsDisabled(t *testing.T) { WalkCatalogsFunc: CatalogWalker(listCatalogs, getPackage), } - ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0", ocv1.UpgradeConstraintPolicyCatalogProvided) _, _, _, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) assert.Contains(t, err.Error(), "no bundles found for package") @@ -1031,7 +1031,7 @@ func TestSomeCatalogsDisabled(t *testing.T) { WalkCatalogsFunc: CatalogWalker(listCatalogs, getPackage), } - ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) + ce := buildFooClusterExtension(pkgName, []string{}, ">=1.0.0", ocv1.UpgradeConstraintPolicyCatalogProvided) gotBundle, gotVersion, _, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) require.NotNil(t, gotBundle) diff --git a/internal/resolve/resolver.go b/internal/resolve/resolver.go index de9b952b01..625111d631 100644 --- a/internal/resolve/resolver.go +++ b/internal/resolve/resolver.go @@ -7,15 +7,15 @@ import ( "github.com/operator-framework/operator-registry/alpha/declcfg" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" ) type Resolver interface { - Resolve(ctx context.Context, ext *ocv1alpha1.ClusterExtension, installedBundle *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) + Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) } -type Func func(ctx context.Context, ext *ocv1alpha1.ClusterExtension, installedBundle *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) +type Func func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) -func (f Func) Resolve(ctx context.Context, ext *ocv1alpha1.ClusterExtension, installedBundle *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { +func (f Func) Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) { return f(ctx, ext, installedBundle) } diff --git a/internal/scheme/scheme.go b/internal/scheme/scheme.go index 933d89b052..f247d9e7ed 100644 --- a/internal/scheme/scheme.go +++ b/internal/scheme/scheme.go @@ -9,14 +9,14 @@ import ( catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" ) var Scheme = runtime.NewScheme() func init() { utilruntime.Must(clientgoscheme.AddToScheme(Scheme)) - utilruntime.Must(ocv1alpha1.AddToScheme(Scheme)) + utilruntime.Must(ocv1.AddToScheme(Scheme)) utilruntime.Must(catalogd.AddToScheme(Scheme)) utilruntime.Must(appsv1.AddToScheme(Scheme)) utilruntime.Must(corev1.AddToScheme(Scheme)) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 2a93a1de4e..d590ee789e 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -30,7 +30,7 @@ import ( catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" ) const ( @@ -192,7 +192,7 @@ func createClusterRoleAndBindingForSA(ctx context.Context, name string, sa *core return nil } -func testInit(t *testing.T) (*ocv1alpha1.ClusterExtension, *catalogd.ClusterCatalog, *corev1.ServiceAccount, *corev1.Namespace) { +func testInit(t *testing.T) (*ocv1.ClusterExtension, *catalogd.ClusterCatalog, *corev1.ServiceAccount, *corev1.Namespace) { var err error clusterExtensionName := fmt.Sprintf("clusterextension-%s", rand.String(8)) @@ -200,7 +200,7 @@ func testInit(t *testing.T) (*ocv1alpha1.ClusterExtension, *catalogd.ClusterCata ns, err := createNamespace(context.Background(), clusterExtensionName) require.NoError(t, err) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: clusterExtensionName, }, @@ -249,7 +249,7 @@ func ensureNoExtensionResources(t *testing.T, clusterExtensionName string) { }, 2*pollDuration, pollInterval) } -func testCleanup(t *testing.T, cat *catalogd.ClusterCatalog, clusterExtension *ocv1alpha1.ClusterExtension, sa *corev1.ServiceAccount, ns *corev1.Namespace) { +func testCleanup(t *testing.T, cat *catalogd.ClusterCatalog, clusterExtension *ocv1.ClusterExtension, sa *corev1.ServiceAccount, ns *corev1.Namespace) { t.Logf("By deleting ClusterCatalog %q", cat.Name) require.NoError(t, c.Delete(context.Background(), cat)) require.Eventually(t, func() bool { @@ -260,7 +260,7 @@ func testCleanup(t *testing.T, cat *catalogd.ClusterCatalog, clusterExtension *o t.Logf("By deleting ClusterExtension %q", clusterExtension.Name) require.NoError(t, c.Delete(context.Background(), clusterExtension)) require.Eventually(t, func() bool { - err := c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, &ocv1alpha1.ClusterExtension{}) + err := c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, &ocv1.ClusterExtension{}) return errors.IsNotFound(err) }, pollDuration, pollInterval) @@ -308,10 +308,10 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + clusterExtension.Spec = ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: tc.packageName, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, @@ -319,7 +319,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { }, }, Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, } @@ -335,20 +335,20 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { t.Log("By eventually reporting progressing as True") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) t.Log("By eventually installing the package successfully") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle) } @@ -364,15 +364,15 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + clusterExtension.Spec = ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: "prometheus", }, }, Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, } @@ -388,10 +388,10 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { t.Log("By eventually reporting Progressing == True and Reason Retrying") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) + assert.Equal(ct, ocv1.ReasonRetrying, cond.Reason) assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [operatorhubio test-catalog]") } }, pollDuration, pollInterval) @@ -406,17 +406,17 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { defer getArtifactsOutput(t) t.Log("By creating an ClusterExtension at a specified version") - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + clusterExtension.Spec = ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: "prometheus", Version: "1.0.0", // No Selector since this is an exact version match }, }, Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, } @@ -425,17 +425,17 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) assert.Equal(ct, - &ocv1alpha1.ClusterExtensionInstallStatus{Bundle: ocv1alpha1.BundleMetadata{ + &ocv1.ClusterExtensionInstallStatus{Bundle: ocv1.BundleMetadata{ Name: "prometheus-operator.1.0.0", Version: "1.0.0", }}, clusterExtension.Status.Install, ) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -452,9 +452,9 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { t.Log("By eventually reporting Progressing == True and Reason Retrying") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) + assert.Equal(ct, ocv1.ReasonRetrying, cond.Reason) assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"prometheus\" matching version \"1.2.0\"", cond.Message) } }, pollDuration, pollInterval) @@ -469,16 +469,16 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { defer getArtifactsOutput(t) t.Log("By creating an ClusterExtension at a specified version") - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + clusterExtension.Spec = ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: "prometheus", Version: "1.0.0", }, }, Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, } @@ -486,10 +486,10 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { t.Log("By eventually reporting a successful resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -497,15 +497,15 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { t.Log("By updating the ClusterExtension resource to a non-successor version") // 1.2.0 does not replace/skip/skipRange 1.0.0. clusterExtension.Spec.Source.Catalog.Version = "1.2.0" - clusterExtension.Spec.Source.Catalog.UpgradeConstraintPolicy = ocv1alpha1.UpgradeConstraintPolicySelfCertified + clusterExtension.Spec.Source.Catalog.UpgradeConstraintPolicy = ocv1.UpgradeConstraintPolicySelfCertified require.NoError(t, c.Update(context.Background(), clusterExtension)) t.Log("By eventually reporting a satisfiable resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } @@ -518,16 +518,16 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { defer getArtifactsOutput(t) t.Log("By creating an ClusterExtension at a specified version") - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + clusterExtension.Spec = ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: "prometheus", Version: "1.0.0", }, }, Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, } @@ -535,10 +535,10 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { t.Log("By eventually reporting a successful resolution") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -550,10 +550,10 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } @@ -565,10 +565,10 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + clusterExtension.Spec = ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: "prometheus", Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ @@ -582,7 +582,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { }, }, Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, } @@ -593,10 +593,10 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { t.Log("By reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -617,10 +617,10 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } @@ -640,7 +640,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { extensionCatalog, err := createTestCatalog(context.Background(), testCatalogName, latestCatalogImage) require.NoError(t, err) clusterExtensionName := fmt.Sprintf("clusterextension-%s", rand.String(8)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: clusterExtensionName, }, @@ -652,10 +652,10 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + clusterExtension.Spec = ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: "prometheus", Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, @@ -663,7 +663,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { }, }, Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, } @@ -674,10 +674,10 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { t.Log("By reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -698,10 +698,10 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { t.Log("By eventually reporting a successful resolution and bundle path") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } @@ -713,10 +713,10 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + clusterExtension.Spec = ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: "prometheus", Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, @@ -724,7 +724,7 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T }, }, Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, } @@ -735,10 +735,10 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T t.Log("By reporting a successful installation") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") } }, pollDuration, pollInterval) @@ -776,10 +776,10 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + clusterExtension.Spec = ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: "prometheus", Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, @@ -787,7 +787,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes }, }, Namespace: ns.Name, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, } @@ -803,20 +803,20 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes t.Log("By eventually reporting Progressing == True with Reason Retrying") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonRetrying, cond.Reason) + assert.Equal(ct, ocv1.ReasonRetrying, cond.Reason) } }, pollDuration, pollInterval) t.Log("By eventually failing to install the package successfully due to insufficient ServiceAccount permissions") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonFailed, cond.Reason) + assert.Equal(ct, ocv1.ReasonFailed, cond.Reason) assert.Equal(ct, "No bundle installed", cond.Message) } }, pollDuration, pollInterval) @@ -831,10 +831,10 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes t.Log("By eventually installing the package successfully") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") assert.NotEmpty(ct, clusterExtension.Status.Install) } @@ -843,10 +843,10 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes t.Log("By eventually reporting Progressing == True with Reason Success") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) } @@ -882,7 +882,7 @@ func getArtifactsOutput(t *testing.T) { } // get all cluster extensions save them to the artifact path. - clusterExtensions := ocv1alpha1.ClusterExtensionList{} + clusterExtensions := ocv1.ClusterExtensionList{} if err := c.List(context.Background(), &clusterExtensions, client.InNamespace("")); err != nil { fmt.Printf("Failed to list cluster extensions: %v", err) } diff --git a/test/extension-developer-e2e/extension_developer_test.go b/test/extension-developer-e2e/extension_developer_test.go index bb6bdad395..1ee2fffe4d 100644 --- a/test/extension-developer-e2e/extension_developer_test.go +++ b/test/extension-developer-e2e/extension_developer_test.go @@ -20,7 +20,7 @@ import ( catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" ) func TestExtensionDeveloper(t *testing.T) { @@ -30,7 +30,7 @@ func TestExtensionDeveloper(t *testing.T) { scheme := runtime.NewScheme() require.NoError(t, catalogd.AddToScheme(scheme)) - require.NoError(t, ocv1alpha1.AddToScheme(scheme)) + require.NoError(t, ocv1.AddToScheme(scheme)) require.NoError(t, corev1.AddToScheme(scheme)) require.NoError(t, rbacv1.AddToScheme(scheme)) @@ -64,19 +64,19 @@ func TestExtensionDeveloper(t *testing.T) { } require.NoError(t, c.Create(ctx, sa)) - clusterExtension := &ocv1alpha1.ClusterExtension{ + clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "registryv1", }, - Spec: ocv1alpha1.ClusterExtensionSpec{ - Source: ocv1alpha1.SourceConfig{ + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ SourceType: "Catalog", - Catalog: &ocv1alpha1.CatalogSource{ + Catalog: &ocv1.CatalogSource{ PackageName: os.Getenv("REG_PKG_NAME"), }, }, Namespace: installNamespace, - ServiceAccount: ocv1alpha1.ServiceAccountReference{ + ServiceAccount: ocv1.ServiceAccountReference{ Name: sa.Name, }, }, @@ -200,14 +200,14 @@ func TestExtensionDeveloper(t *testing.T) { require.NoError(t, c.Create(context.Background(), clusterExtension)) t.Log("It should have a status condition type of Installed with a status of True and a reason of Success") require.EventuallyWithT(t, func(ct *assert.CollectT) { - ext := &ocv1alpha1.ClusterExtension{} + ext := &ocv1.ClusterExtension{} assert.NoError(ct, c.Get(context.Background(), client.ObjectKeyFromObject(clusterExtension), ext)) - cond := meta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled) + cond := meta.FindStatusCondition(ext.Status.Conditions, ocv1.TypeInstalled) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) }, 2*time.Minute, time.Second) require.NoError(t, c.Delete(context.Background(), catalog)) require.NoError(t, c.Delete(context.Background(), clusterExtension)) diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go index 78f7284a8c..d6f46ca948 100644 --- a/test/upgrade-e2e/post_upgrade_test.go +++ b/test/upgrade-e2e/post_upgrade_test.go @@ -20,7 +20,7 @@ import ( catalogdv1alpha1 "github.com/operator-framework/catalogd/api/core/v1alpha1" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + ocv1 "github.com/operator-framework/operator-controller/api/v1" ) func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { @@ -76,15 +76,15 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { }, time.Minute, time.Second) t.Log("Checking that the ClusterExtension is installed") - var clusterExtension ocv1alpha1.ClusterExtension + var clusterExtension ocv1.ClusterExtension require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(ctx, types.NamespacedName{Name: testClusterExtensionName}, &clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") if assert.NotNil(ct, clusterExtension.Status.Install) { assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle.Version) @@ -101,13 +101,13 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { t.Log("Checking that the ClusterExtension installs successfully") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(ctx, types.NamespacedName{Name: testClusterExtensionName}, &clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeInstalled) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) if !assert.NotNil(ct, cond) { return } - assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) + assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") - assert.Equal(ct, ocv1alpha1.BundleMetadata{Name: "prometheus-operator.1.0.1", Version: "1.0.1"}, clusterExtension.Status.Install.Bundle) + assert.Equal(ct, ocv1.BundleMetadata{Name: "prometheus-operator.1.0.1", Version: "1.0.1"}, clusterExtension.Status.Install.Bundle) assert.NotEqual(ct, previousVersion, clusterExtension.Status.Install.Bundle.Version) }, time.Minute, time.Second) } From 32498f12f75a57ea4d2f6bd8b3d02b832ea96960 Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Tue, 12 Nov 2024 23:04:41 +0100 Subject: [PATCH 117/694] :warning: Bump catalogd to v1 (#1441) * Bump catalogd to v1 Signed-off-by: Per Goncalves da Silva * update catalogd dep to v1.0.0-rc1 Signed-off-by: everettraven * run make verify to regenerate generated content Signed-off-by: everettraven --------- Signed-off-by: Per Goncalves da Silva Signed-off-by: everettraven Co-authored-by: Per Goncalves da Silva Co-authored-by: everettraven --- cmd/manager/main.go | 2 +- config/samples/catalogd_operatorcatalog.yaml | 2 +- docs/api-reference/catalogd-api-reference.md | 18 +++++------------- docs/concepts/controlling-catalog-selection.md | 6 +++--- docs/getting-started/olmv1_getting_started.md | 2 +- docs/tutorials/add-catalog.md | 6 +++--- go.mod | 2 +- go.sum | 4 ++-- hack/test/pre-upgrade-setup.sh | 2 +- internal/catalogmetadata/client/client.go | 2 +- internal/catalogmetadata/client/client_test.go | 2 +- .../controllers/clustercatalog_controller.go | 2 +- .../clustercatalog_controller_test.go | 2 +- .../controllers/clusterextension_controller.go | 2 +- internal/resolve/catalog.go | 2 +- internal/resolve/catalog_test.go | 2 +- internal/scheme/scheme.go | 2 +- test/e2e/cluster_extension_install_test.go | 2 +- test/e2e/e2e_suite_test.go | 2 +- .../extension_developer_test.go | 2 +- test/upgrade-e2e/post_upgrade_test.go | 8 ++++---- 21 files changed, 33 insertions(+), 41 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 563e514795..38d7534f37 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -46,7 +46,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/metrics/server" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" ocv1 "github.com/operator-framework/operator-controller/api/v1" diff --git a/config/samples/catalogd_operatorcatalog.yaml b/config/samples/catalogd_operatorcatalog.yaml index 8657f7a702..4ce96d5b38 100644 --- a/config/samples/catalogd_operatorcatalog.yaml +++ b/config/samples/catalogd_operatorcatalog.yaml @@ -1,4 +1,4 @@ -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterCatalog metadata: name: operatorhubio diff --git a/docs/api-reference/catalogd-api-reference.md b/docs/api-reference/catalogd-api-reference.md index 83c1558355..b313d26468 100644 --- a/docs/api-reference/catalogd-api-reference.md +++ b/docs/api-reference/catalogd-api-reference.md @@ -1,20 +1,12 @@ # API Reference ## Packages -- [olm.operatorframework.io/core](#olmoperatorframeworkiocore) -- [olm.operatorframework.io/v1alpha1](#olmoperatorframeworkiov1alpha1) +- [olm.operatorframework.io/v1](#olmoperatorframeworkiov1) -## olm.operatorframework.io/core +## olm.operatorframework.io/v1 -Package api is the internal version of the API. - - - - -## olm.operatorframework.io/v1alpha1 - -Package v1alpha1 contains API Schema definitions for the core v1alpha1 API group +Package v1 contains API Schema definitions for the core v1 API group ### Resource Types - [ClusterCatalog](#clustercatalog) @@ -71,7 +63,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `apiVersion` _string_ | `olm.operatorframework.io/v1alpha1` | | | +| `apiVersion` _string_ | `olm.operatorframework.io/v1` | | | | `kind` _string_ | `ClusterCatalog` | | | | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | @@ -92,7 +84,7 @@ ClusterCatalogList contains a list of ClusterCatalog | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `apiVersion` _string_ | `olm.operatorframework.io/v1alpha1` | | | +| `apiVersion` _string_ | `olm.operatorframework.io/v1` | | | | `kind` _string_ | `ClusterCatalogList` | | | | `kind` _string_ | 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 | | | | `apiVersion` _string_ | 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 | | | diff --git a/docs/concepts/controlling-catalog-selection.md b/docs/concepts/controlling-catalog-selection.md index ec987e5567..a6659f77ee 100644 --- a/docs/concepts/controlling-catalog-selection.md +++ b/docs/concepts/controlling-catalog-selection.md @@ -125,7 +125,7 @@ When multiple catalogs provide the same package, you can set priorities to resol In your `ClusterCatalog` resource, set the `priority` field: ```yaml -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterCatalog metadata: name: high-priority-catalog @@ -160,7 +160,7 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera 1. **Create or Update `ClusterCatalogs` with Appropriate Labels and Priority** ```yaml - apiVersion: olm.operatorframework.io/v1alpha1 + apiVersion: olm.operatorframework.io/v1 kind: ClusterCatalog metadata: name: catalog-a @@ -175,7 +175,7 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera ``` ```yaml - apiVersion: olm.operatorframework.io/v1alpha1 + apiVersion: olm.operatorframework.io/v1 kind: ClusterCatalog metadata: name: catalog-b diff --git a/docs/getting-started/olmv1_getting_started.md b/docs/getting-started/olmv1_getting_started.md index efffddde88..49ec4a137c 100644 --- a/docs/getting-started/olmv1_getting_started.md +++ b/docs/getting-started/olmv1_getting_started.md @@ -35,7 +35,7 @@ To create the catalog, run the following command: ```bash # Create ClusterCatalog kubectl apply -f - < - API Version: olm.operatorframework.io/v1alpha1 + API Version: olm.operatorframework.io/v1 Kind: ClusterCatalog Metadata: Creation Timestamp: 2024-10-02T19:51:24Z diff --git a/go.mod b/go.mod index 28624580a2..00c04ee424 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/onsi/gomega v1.35.1 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/catalogd v0.36.0 + github.com/operator-framework/catalogd v1.0.0-rc1 github.com/operator-framework/helm-operator-plugins v0.7.0 github.com/operator-framework/operator-registry v1.48.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index b0a62ef63e..266c4f34e8 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eT github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/catalogd v0.36.0 h1:4ySpKitCooKPpTqQon/2dIGR7oEOIee8WYrRYwwQZ00= -github.com/operator-framework/catalogd v0.36.0/go.mod h1:ERq4C2ksfkf3wu3XmtGP2fIkBSqS6LfaHhtcSEcU7Ww= +github.com/operator-framework/catalogd v1.0.0-rc1 h1:wuM8yLy52lwrfyVJJ++l8zV+pxJOnuQLC84fO0UTbts= +github.com/operator-framework/catalogd v1.0.0-rc1/go.mod h1:ERq4C2ksfkf3wu3XmtGP2fIkBSqS6LfaHhtcSEcU7Ww= github.com/operator-framework/helm-operator-plugins v0.7.0 h1:YmtIWFc9BaNaDc5mk/dkG0P2BqPZOqpDvjWih5Fczuk= github.com/operator-framework/helm-operator-plugins v0.7.0/go.mod h1:fUUCJR3bWtMBZ1qdDhbwjacsBHi9uT576tF4u/DwOgQ= github.com/operator-framework/operator-lib v0.15.0 h1:0QeRM4PMtThqINpcFGCEBnIV3Z8u7/8fYLEx6mUtdcM= diff --git a/hack/test/pre-upgrade-setup.sh b/hack/test/pre-upgrade-setup.sh index 6256944ac0..11bcfcddb1 100755 --- a/hack/test/pre-upgrade-setup.sh +++ b/hack/test/pre-upgrade-setup.sh @@ -20,7 +20,7 @@ TEST_CLUSTER_CATALOG_NAME=$2 TEST_CLUSTER_EXTENSION_NAME=$3 kubectl apply -f - << EOF -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterCatalog metadata: name: ${TEST_CLUSTER_CATALOG_NAME} diff --git a/internal/catalogmetadata/client/client.go b/internal/catalogmetadata/client/client.go index a68c6c9896..4b75e6291b 100644 --- a/internal/catalogmetadata/client/client.go +++ b/internal/catalogmetadata/client/client.go @@ -12,7 +12,7 @@ import ( "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" "github.com/operator-framework/operator-registry/alpha/declcfg" ) diff --git a/internal/catalogmetadata/client/client_test.go b/internal/catalogmetadata/client/client_test.go index 15430d7c1f..16adb94a0a 100644 --- a/internal/catalogmetadata/client/client_test.go +++ b/internal/catalogmetadata/client/client_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" "github.com/operator-framework/operator-registry/alpha/declcfg" catalogClient "github.com/operator-framework/operator-controller/internal/catalogmetadata/client" diff --git a/internal/controllers/clustercatalog_controller.go b/internal/controllers/clustercatalog_controller.go index dc86ed59fa..daf2668c50 100644 --- a/internal/controllers/clustercatalog_controller.go +++ b/internal/controllers/clustercatalog_controller.go @@ -25,7 +25,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" ) type CatalogCache interface { diff --git a/internal/controllers/clustercatalog_controller_test.go b/internal/controllers/clustercatalog_controller_test.go index 639f20c963..fb190b4703 100644 --- a/internal/controllers/clustercatalog_controller_test.go +++ b/internal/controllers/clustercatalog_controller_test.go @@ -14,7 +14,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" "github.com/operator-framework/operator-controller/internal/controllers" "github.com/operator-framework/operator-controller/internal/scheme" diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index ede0a971c7..dea94a7f21 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -45,7 +45,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/operator-framework/api/pkg/operators/v1alpha1" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" "github.com/operator-framework/operator-registry/alpha/declcfg" diff --git a/internal/resolve/catalog.go b/internal/resolve/catalog.go index 8597710556..944744c5f2 100644 --- a/internal/resolve/catalog.go +++ b/internal/resolve/catalog.go @@ -15,7 +15,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" "github.com/operator-framework/operator-registry/alpha/declcfg" ocv1 "github.com/operator-framework/operator-controller/api/v1" diff --git a/internal/resolve/catalog_test.go b/internal/resolve/catalog_test.go index b59733e70f..83eeba9b00 100644 --- a/internal/resolve/catalog_test.go +++ b/internal/resolve/catalog_test.go @@ -16,7 +16,7 @@ import ( "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" diff --git a/internal/scheme/scheme.go b/internal/scheme/scheme.go index f247d9e7ed..a5fae62988 100644 --- a/internal/scheme/scheme.go +++ b/internal/scheme/scheme.go @@ -7,7 +7,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" ocv1 "github.com/operator-framework/operator-controller/api/v1" ) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index d590ee789e..cecb7f2191 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -28,7 +28,7 @@ import ( "k8s.io/utils/env" "sigs.k8s.io/controller-runtime/pkg/client" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" ocv1 "github.com/operator-framework/operator-controller/api/v1" ) diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index e5e9378226..df6d3fdd94 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -13,7 +13,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" "github.com/operator-framework/operator-controller/internal/scheme" ) diff --git a/test/extension-developer-e2e/extension_developer_test.go b/test/extension-developer-e2e/extension_developer_test.go index 1ee2fffe4d..6974983a0e 100644 --- a/test/extension-developer-e2e/extension_developer_test.go +++ b/test/extension-developer-e2e/extension_developer_test.go @@ -18,7 +18,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" ocv1 "github.com/operator-framework/operator-controller/api/v1" ) diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go index d6f46ca948..ace977d130 100644 --- a/test/upgrade-e2e/post_upgrade_test.go +++ b/test/upgrade-e2e/post_upgrade_test.go @@ -18,7 +18,7 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - catalogdv1alpha1 "github.com/operator-framework/catalogd/api/core/v1alpha1" + catalogd "github.com/operator-framework/catalogd/api/v1" ocv1 "github.com/operator-framework/operator-controller/api/v1" ) @@ -65,14 +65,14 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { t.Log("Checking that the ClusterCatalog is serving") require.EventuallyWithT(t, func(ct *assert.CollectT) { - var clusterCatalog catalogdv1alpha1.ClusterCatalog + var clusterCatalog catalogd.ClusterCatalog assert.NoError(ct, c.Get(ctx, types.NamespacedName{Name: testClusterCatalogName}, &clusterCatalog)) - cond := apimeta.FindStatusCondition(clusterCatalog.Status.Conditions, catalogdv1alpha1.TypeServing) + cond := apimeta.FindStatusCondition(clusterCatalog.Status.Conditions, catalogd.TypeServing) if !assert.NotNil(ct, cond) { return } assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, catalogdv1alpha1.ReasonAvailable, cond.Reason) + assert.Equal(ct, catalogd.ReasonAvailable, cond.Reason) }, time.Minute, time.Second) t.Log("Checking that the ClusterExtension is installed") From a5966fa0ff000f4d4b07baf3a890d8cf679e5d7e Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:52:21 +0100 Subject: [PATCH 118/694] Fix upgrade-e2e CI job (#1451) The latest release v1.0.0-rc1 includes the change to the API: OLM v1alpha1 APIs became v1 and this needs to be reflected in the upgrade-e2e CI job. Refs: * https://github.com/operator-framework/operator-controller/pull/1228 * https://github.com/operator-framework/operator-controller/pull/1441 Signed-off-by: Mikalai Radchuk --- hack/test/pre-upgrade-setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/test/pre-upgrade-setup.sh b/hack/test/pre-upgrade-setup.sh index 11bcfcddb1..33dd035fbd 100755 --- a/hack/test/pre-upgrade-setup.sh +++ b/hack/test/pre-upgrade-setup.sh @@ -127,7 +127,7 @@ roleRef: EOF kubectl apply -f - << EOF -apiVersion: olm.operatorframework.io/v1alpha1 +apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: ${TEST_CLUSTER_EXTENSION_NAME} From 102f29fa0cd87151e2f6d4595d320528e54e81ea Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:20:26 +0100 Subject: [PATCH 119/694] Update remaining v1alpha1 references with v1 (#1452) There are a few remaining references to v1alpha1 that need to be updated to use v1. Ref: * https://github.com/operator-framework/operator-controller/pull/1228 Signed-off-by: Mikalai Radchuk --- api/v1/groupversion_info.go | 2 +- config/samples/kustomization.yaml | 1 - .../api-reference/operator-controller-api-reference.md | 2 +- docs/getting-started/olmv1_getting_started.md | 2 +- docs/howto/derive-service-account.md | 10 +++++----- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/api/v1/groupversion_info.go b/api/v1/groupversion_info.go index fdd9174aca..f2e8582ee5 100644 --- a/api/v1/groupversion_info.go +++ b/api/v1/groupversion_info.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1alpha1 contains API Schema definitions for the olm v1alpha1 API group +// Package v1 contains API Schema definitions for the olm v1 API group // +kubebuilder:object:generate=true // +groupName=olm.operatorframework.io package v1 diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 7816e8fefb..2a64d96c2d 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,5 +1,4 @@ ## Append samples of your project ## resources: - olm_v1_clusterextension.yaml -- olm_v1alpha1_extension.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/docs/api-reference/operator-controller-api-reference.md b/docs/api-reference/operator-controller-api-reference.md index 63ea96df6f..c3a3862b15 100644 --- a/docs/api-reference/operator-controller-api-reference.md +++ b/docs/api-reference/operator-controller-api-reference.md @@ -6,7 +6,7 @@ ## olm.operatorframework.io/v1 -Package v1alpha1 contains API Schema definitions for the olm v1alpha1 API group +Package v1 contains API Schema definitions for the olm v1 API group ### Resource Types - [ClusterExtension](#clusterextension) diff --git a/docs/getting-started/olmv1_getting_started.md b/docs/getting-started/olmv1_getting_started.md index 49ec4a137c..444a43e47d 100644 --- a/docs/getting-started/olmv1_getting_started.md +++ b/docs/getting-started/olmv1_getting_started.md @@ -65,7 +65,7 @@ More information on installing extensions can be found [here](../tutorials/insta ```bash # Apply the sample ClusterExtension. Manifest already includes # namespace and adequately privileged service account -kubectl apply -f https://raw.githubusercontent.com/operator-framework/operator-controller/main/config/samples/olm_v1alpha1_clusterextension.yaml +kubectl apply -f https://raw.githubusercontent.com/operator-framework/operator-controller/main/config/samples/olm_v1_clusterextension.yaml ``` ### Upgrade the Cluster Extension diff --git a/docs/howto/derive-service-account.md b/docs/howto/derive-service-account.md index ac0481ffc7..08ce67929a 100644 --- a/docs/howto/derive-service-account.md +++ b/docs/howto/derive-service-account.md @@ -1,6 +1,6 @@ # Derive minimal ServiceAccount required for ClusterExtension Installation and Management -OLM v1 does not have permission to install extensions on a cluster by default. In order to install a [supported bundle](../project/olmv1_limitations.md), +OLM v1 does not have permission to install extensions on a cluster by default. In order to install a [supported bundle](../project/olmv1_limitations.md), OLM must be provided a ServiceAccount configured with the appropriate permissions. This document serves as a guide for how to derive the RBAC necessary to install a bundle. @@ -31,7 +31,7 @@ Depending on the scope, each permission will need to be added to either a `Clust ### Example The following example illustrates the process of deriving the minimal RBAC required to install the [ArgoCD Operator](https://operatorhub.io/operator/argocd-operator) [v0.6.0](https://operatorhub.io/operator/argocd-operator/alpha/argocd-operator.v0.6.0) provided by [OperatorHub.io](https://operatorhub.io/). -The final permission set can be found in the [ClusterExtension sample manifest](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1alpha1_clusterextension.yaml) in the [samples](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1alpha1_clusterextension.yaml) directory. +The final permission set can be found in the [ClusterExtension sample manifest](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1_clusterextension.yaml) in the [samples](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1_clusterextension.yaml) directory. The bundle includes the following manifests, which can be found [here](https://github.com/argoproj-labs/argocd-operator/tree/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0): @@ -99,7 +99,7 @@ The same can be done for `ClusterRoleBindings`. ##### Step 2. `CustomResourceDefinition` permissions -The installer service account must be able to create and manage the `CustomResourceDefinition`s for the extension, as well +The installer service account must be able to create and manage the `CustomResourceDefinition`s for the extension, as well as grant the extension controller's service account the permissions it needs to manage its CRDs. ```yaml @@ -302,7 +302,7 @@ Once the installer service account required cluster-scoped and namespace-scoped 6. Create the `RoleBinding` between the installer service account and its role 7. Create the `ClusterExtension` -A manifest with the full set of resources can be found [here](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1alpha1_clusterextension.yaml). +A manifest with the full set of resources can be found [here](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1_clusterextension.yaml). ### Alternatives @@ -348,5 +348,5 @@ kubectl create clusterrolebinding my-cluster-extension-installer-role-binding \ In the spirit of making this process more tenable until the proper tools are in place, the scripts in [hack/tools/catalogs](https://github.com/operator-framework/operator-controller/blob/main/hack/tools/catalogs) were created to help the user navigate and search catalogs as well -as to generate the minimal RBAC requirements. These tools are offered as is, with no guarantees on their correctness, +as to generate the minimal RBAC requirements. These tools are offered as is, with no guarantees on their correctness, support, or maintenance. For more information, see [Hack Catalog Tools](https://github.com/operator-framework/operator-controller/blob/main/hack/tools/catalogs/README.md). From ac75d65186d9a8dd5373146c3e79c85b31e9af6a Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk <509198+m1kola@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:22:38 +0100 Subject: [PATCH 120/694] Improve formatting on the docs website (#1446) Fixes a number of formatting/rendering issues. Mostly around lists, admonitions (notes, warnings, etc) and collapsible text. Signed-off-by: Mikalai Radchuk --- CONTRIBUTING.md | 22 +-- .../concepts/controlling-catalog-selection.md | 105 ++++++------ docs/concepts/upgrade-support.md | 19 ++- docs/contribute/developer.md | 52 +++--- docs/getting-started/olmv1_getting_started.md | 6 +- docs/howto/catalog-queries.md | 5 +- docs/howto/derive-service-account.md | 45 ++--- docs/tutorials/downgrade-extension.md | 89 +++++----- docs/tutorials/explore-available-content.md | 63 +++---- docs/tutorials/upgrade-extension.md | 160 +++++++++--------- 10 files changed, 289 insertions(+), 277 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ca434ce1b..284aa1a3e0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,28 +43,28 @@ you can follow the steps below to test your changes: 1. Create the cluster: -```sh -kind create cluster operator-controller -``` + ```sh + kind create cluster operator-controller + ``` 2. Build your changes: -```sh -make build docker-build -``` + ```sh + make build docker-build + ``` 3. Load the image locally and Deploy to Kind -```sh -make kind-load kind-deploy -``` + ```sh + make kind-load kind-deploy + ``` ### Communication Channels - Email: [operator-framework-olm-dev](mailto:operator-framework-olm-dev@googlegroups.com) - Slack: [#olm-dev](https://kubernetes.slack.com/archives/C0181L6JYQ2) - Google Group: [olm-gg](https://groups.google.com/g/operator-framework-olm-dev) -- Weekly in Person Working Group Meeting: [olm-wg](https://github.com/operator-framework/community#operator-lifecycle-manager-working-group) +- Weekly in Person Working Group Meeting: [olm-wg](https://github.com/operator-framework/community#operator-lifecycle-manager-working-group) ## How are Milestones Designed? @@ -91,7 +91,7 @@ As discussed earlier, the operator-controller adheres to a microservice architec ## Submitting Issues -Unsure where to submit an issue? +Unsure where to submit an issue? - [The Operator-Controller project](https://github.com/operator-framework/operator-controller/), which is the top level component allowing users to specify operators they'd like to install. - [The Catalogd project](https://github.com/operator-framework/catalogd/), which hosts operator content and helps users discover installable content. diff --git a/docs/concepts/controlling-catalog-selection.md b/docs/concepts/controlling-catalog-selection.md index a6659f77ee..dc2f90ab8c 100644 --- a/docs/concepts/controlling-catalog-selection.md +++ b/docs/concepts/controlling-catalog-selection.md @@ -159,70 +159,71 @@ If the system cannot resolve to a single bundle due to ambiguity, it will genera 1. **Create or Update `ClusterCatalogs` with Appropriate Labels and Priority** - ```yaml - apiVersion: olm.operatorframework.io/v1 - kind: ClusterCatalog - metadata: - name: catalog-a - labels: - example.com/support: "true" - spec: - priority: 1000 - source: - type: Image - image: - ref: quay.io/example/content-management-a:latest - ``` - - ```yaml - apiVersion: olm.operatorframework.io/v1 - kind: ClusterCatalog - metadata: - name: catalog-b - labels: - example.com/support: "false" - spec: - priority: 500 - source: - type: Image - image: - ref: quay.io/example/content-management-b:latest - ``` - NB: an `olm.operatorframework.io/metadata.name` label will be added automatically to ClusterCatalogs when applied + ```yaml + apiVersion: olm.operatorframework.io/v1 + kind: ClusterCatalog + metadata: + name: catalog-a + labels: + example.com/support: "true" + spec: + priority: 1000 + source: + type: Image + image: + ref: quay.io/example/content-management-a:latest + ``` + + ```yaml + apiVersion: olm.operatorframework.io/v1 + kind: ClusterCatalog + metadata: + name: catalog-b + labels: + example.com/support: "false" + spec: + priority: 500 + source: + type: Image + image: + ref: quay.io/example/content-management-b:latest + ``` + !!! note + An `olm.operatorframework.io/metadata.name` label will be added automatically to ClusterCatalogs when applied 2. **Create a `ClusterExtension` with Catalog Selection** - ```yaml - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtension - metadata: - name: install-my-operator - spec: - packageName: my-operator - catalog: - selector: - matchLabels: - example.com/support: "true" - ``` + ```yaml + apiVersion: olm.operatorframework.io/v1 + kind: ClusterExtension + metadata: + name: install-my-operator + spec: + packageName: my-operator + catalog: + selector: + matchLabels: + example.com/support: "true" + ``` 3. **Apply the Resources** - ```shell - kubectl apply -f content-management-a.yaml - kubectl apply -f content-management-b.yaml - kubectl apply -f install-my-operator.yaml - ``` + ```shell + kubectl apply -f content-management-a.yaml + kubectl apply -f content-management-b.yaml + kubectl apply -f install-my-operator.yaml + ``` 4. **Verify the Installation** - Check the status of the `ClusterExtension`: + Check the status of the `ClusterExtension`: - ```shell - kubectl get clusterextension install-my-operator -o yaml - ``` + ```shell + kubectl get clusterextension install-my-operator -o yaml + ``` - The status should indicate that the bundle was resolved from `catalog-a` due to the higher priority and matching label. + The status should indicate that the bundle was resolved from `catalog-a` due to the higher priority and matching label. ## Important Notes diff --git a/docs/concepts/upgrade-support.md b/docs/concepts/upgrade-support.md index 5abd579f1b..8287ff2b37 100644 --- a/docs/concepts/upgrade-support.md +++ b/docs/concepts/upgrade-support.md @@ -17,10 +17,12 @@ When determining upgrade edges, also known as upgrade paths or upgrade constrain By supporting legacy OLM semantics, OLM v1 now honors the upgrade graph from catalogs accurately. -* If there are multiple possible successors, OLM v1 behavior differs in the following ways: - * In legacy OLM, the successor closest to the channel head is chosen. - * In OLM v1, the successor with the highest semantic version (semver) is chosen. -* Consider the following set of file-based catalog (FBC) channel entries: +If there are multiple possible successors, OLM v1 behavior differs in the following ways: + +* In legacy OLM, the successor closest to the channel head is chosen. +* In OLM v1, the successor with the highest semantic version (semver) is chosen. + +Consider the following set of file-based catalog (FBC) channel entries: ```yaml # ... @@ -51,7 +53,7 @@ spec: version: "" ``` -where setting the `upgradeConstraintPolicy` to: +Setting the `upgradeConstraintPolicy` to: `SelfCertified` : Does not limit the next version to the set of successors, and instead allows for any downgrade, sidegrade, or upgrade. @@ -63,8 +65,8 @@ where setting the `upgradeConstraintPolicy` to: OLM supports Semver to provide a simplified way for package authors to define compatible upgrades. According to the Semver standard, releases within a major version (e.g. `>=1.0.0 <2.0.0`) must be compatible. As a result, package authors can publish a new package version following the Semver specification, and OLM assumes compatibility. Package authors do not have to explicitly define upgrade edges in the catalog. -> [!NOTE] -> Currently, OLM 1.0 does not support automatic upgrades to the next major version. You must manually verify and perform major version upgrades. For more information about major version upgrades, see [Manually verified upgrades and downgrades](#manually-verified-upgrades-and-downgrades). +!!! note + Currently, OLM 1.0 does not support automatic upgrades to the next major version. You must manually verify and perform major version upgrades. For more information about major version upgrades, see [Manually verified upgrades and downgrades](#manually-verified-upgrades-and-downgrades). ### Upgrades within the major version zero @@ -77,7 +79,8 @@ You must verify and perform upgrades manually in cases where automatic upgrades ## Manually verified upgrades and downgrades -**Warning:** If you want to force an upgrade manually, you must thoroughly verify the outcome before applying any changes to production workloads. Failure to test and verify the upgrade might lead to catastrophic consequences such as data loss. +!!! warning + If you want to force an upgrade manually, you must thoroughly verify the outcome before applying any changes to production workloads. Failure to test and verify the upgrade might lead to catastrophic consequences such as data loss. As a package admin, if you must upgrade or downgrade to version that might be incompatible with the currently installed version, you can set the `.spec.upgradeConstraintPolicy` field to `SelfCertified` on the relevant `ClusterExtension` resource. diff --git a/docs/contribute/developer.md b/docs/contribute/developer.md index b97c9d693b..8a63f0d7c5 100644 --- a/docs/contribute/developer.md +++ b/docs/contribute/developer.md @@ -3,10 +3,10 @@ The following `make run` starts a [KIND](https://sigs.k8s.io/kind) cluster for you to get a local cluster for testing, see the manual install steps below for how to run against a remote cluster. -> [!NOTE] -> You will need a container runtime environment, like Docker, or experimentally, Podman, installed, to run Kind. -> -> If you are on MacOS, see [Special Setup for MacOS](#special-setup-for-macos). +!!! note + You will need a container runtime environment, like Docker, or experimentally, Podman, installed, to run Kind. + + If you are on MacOS, see [Special Setup for MacOS](#special-setup-for-macos). ### Quickstart Installation @@ -20,9 +20,9 @@ This will build a local container image of the operator-controller, create a new ### To Install Any Given Release -> [!CAUTION] -> Operator-Controller depends on [cert-manager](https://cert-manager.io/). Running the following command -> may affect an existing installation of cert-manager and cause cluster instability. +!!! warning + Operator-Controller depends on [cert-manager](https://cert-manager.io/). Running the following command + may affect an existing installation of cert-manager and cause cluster instability. The latest version of Operator Controller can be installed with the following command: @@ -33,21 +33,21 @@ curl -L -s https://github.com/operator-framework/operator-controller/releases/la ### Manual Step-by-Step Installation 1. Install Instances of Custom Resources: -```sh -kubectl apply -f config/samples/ -``` + ```sh + kubectl apply -f config/samples/ + ``` 2. Build and push your image to the location specified by `IMG`: -```sh -make docker-build docker-push IMG=/operator-controller:tag -``` + ```sh + make docker-build docker-push IMG=/operator-controller:tag + ``` 3. Deploy the controller to the cluster with the image specified by `IMG`: -```sh -make deploy IMG=/operator-controller:tag -``` + ```sh + make deploy IMG=/operator-controller:tag + ``` ### Uninstall CRDs To delete the CRDs from the cluster: @@ -72,7 +72,8 @@ make manifests --- -**NOTE:** Run `make help` for more information on all potential `make` targets. +!!! note + Run `make help` for more information on all potential `make` targets. ### Rapid Iterative Development with Tilt @@ -124,17 +125,18 @@ This is typically as short as: tilt up ``` -**NOTE:** if you are using Podman, at least as of v4.5.1, you need to do this: +!!! note + If you are using Podman, at least as of v4.5.1, you need to do this: -```shell -DOCKER_BUILDKIT=0 tilt up -``` + ```shell + DOCKER_BUILDKIT=0 tilt up + ``` -Otherwise, you'll see an error when Tilt tries to build your image that looks similar to: + Otherwise, you'll see an error when Tilt tries to build your image that looks similar to: -```text -Build Failed: ImageBuild: stat /var/tmp/libpod_builder2384046170/build/Dockerfile: no such file or directory -``` + ```text + Build Failed: ImageBuild: stat /var/tmp/libpod_builder2384046170/build/Dockerfile: no such file or directory + ``` When Tilt starts, you'll see something like this in your terminal: diff --git a/docs/getting-started/olmv1_getting_started.md b/docs/getting-started/olmv1_getting_started.md index 444a43e47d..0763f9263c 100644 --- a/docs/getting-started/olmv1_getting_started.md +++ b/docs/getting-started/olmv1_getting_started.md @@ -2,9 +2,9 @@ The following script will install OLMv1 on a Kubernetes cluster. If you don't have one, you can deploy a Kubernetes cluster with [KIND](https://sigs.k8s.io/kind). -> [!CAUTION] -> Operator-Controller depends on [cert-manager](https://cert-manager.io/). Running the following command -> may affect an existing installation of cert-manager and cause cluster instability. +!!! warning + Operator-Controller depends on [cert-manager](https://cert-manager.io/). Running the following command + may affect an existing installation of cert-manager and cause cluster instability. The latest version of Operator Controller can be installed with the following command: diff --git a/docs/howto/catalog-queries.md b/docs/howto/catalog-queries.md index 7eadb85019..c58a7bff15 100644 --- a/docs/howto/catalog-queries.md +++ b/docs/howto/catalog-queries.md @@ -1,7 +1,8 @@ # Catalog queries -**Note:** By default, Catalogd is installed with TLS enabled for the catalog webserver. -The following examples will show this default behavior, but for simplicity's sake will ignore TLS verification in the curl commands using the `-k` flag. +!!! note + By default, Catalogd is installed with TLS enabled for the catalog webserver. + The following examples will show this default behavior, but for simplicity's sake will ignore TLS verification in the curl commands using the `-k` flag. You can use the `curl` command with `jq` to query catalogs that are installed on your cluster. diff --git a/docs/howto/derive-service-account.md b/docs/howto/derive-service-account.md index 08ce67929a..15e82605b6 100644 --- a/docs/howto/derive-service-account.md +++ b/docs/howto/derive-service-account.md @@ -5,7 +5,7 @@ OLM must be provided a ServiceAccount configured with the appropriate permission This document serves as a guide for how to derive the RBAC necessary to install a bundle. -### Required RBAC +## Required RBAC The required permissions for the installation and management of a cluster extension can be determined by examining the contents of its bundle image. This bundle image contains all the manifests that make up the extension (e.g. `CustomResourceDefinition`s, `Service`s, `Secret`s, `ConfigMap`s, `Deployment`s etc.) @@ -28,7 +28,7 @@ Keep in mind, that it is not possible to scope `create`, `list`, and `watch` per Depending on the scope, each permission will need to be added to either a `ClusterRole` or a `Role` and then bound to the service account with a `ClusterRoleBinding` or a `RoleBinding`. -### Example +## Example The following example illustrates the process of deriving the minimal RBAC required to install the [ArgoCD Operator](https://operatorhub.io/operator/argocd-operator) [v0.6.0](https://operatorhub.io/operator/argocd-operator/alpha/argocd-operator.v0.6.0) provided by [OperatorHub.io](https://operatorhub.io/). The final permission set can be found in the [ClusterExtension sample manifest](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1_clusterextension.yaml) in the [samples](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1_clusterextension.yaml) directory. @@ -51,9 +51,9 @@ The bundle includes the following manifests, which can be found [here](https://g The `ClusterServiceVersion` defines a single `Deployment` in `spec.install.deployments` named `argocd-operator-controller-manager` with a `ServiceAccount` of the same name. It declares the following cluster-scoped permissions in `spec.install.clusterPermissions`, and its namespace-scoped permissions in `spec.install.permissions`. -#### Derive permissions for the installer service account `ClusterRole` +### Derive permissions for the installer service account `ClusterRole` -##### Step 1. RBAC creation and management permissions +#### Step 1. RBAC creation and management permissions The installer service account must create and manage the `ClusterRole`s and `ClusterRoleBinding`s for the extension controller(s). Therefore, it must have the following permissions: @@ -75,10 +75,11 @@ Therefore, it must have the following permissions: resourceNames: [, ...] ``` -Note: The `resourceNames` field should be populated with the names of the `ClusterRole`s and `ClusterRoleBinding`s created by OLM v1. -These names are generated with the following format: `.`. Since it is not a trivial task -to generate these names ahead of time, it is recommended to use a wildcard `*` in the `resourceNames` field for the installation. -Then, update the `resourceNames` fields by inspecting the cluster for the generated resource names. For instance, for `ClusterRole`s: +!!! note + The `resourceNames` field should be populated with the names of the `ClusterRole`s and `ClusterRoleBinding`s created by OLM v1. + These names are generated with the following format: `.`. Since it is not a trivial task + to generate these names ahead of time, it is recommended to use a wildcard `*` in the `resourceNames` field for the installation. + Then, update the `resourceNames` fields by inspecting the cluster for the generated resource names. For instance, for `ClusterRole`s: ```terminal kubectl get clusterroles | grep argocd @@ -97,7 +98,7 @@ argocd-operator.v0-22gmilmgp91wu25is5i2ec598hni8owq3l71bbkl7iz3 2024-09-3 The same can be done for `ClusterRoleBindings`. -##### Step 2. `CustomResourceDefinition` permissions +#### Step 2. `CustomResourceDefinition` permissions The installer service account must be able to create and manage the `CustomResourceDefinition`s for the extension, as well as grant the extension controller's service account the permissions it needs to manage its CRDs. @@ -113,7 +114,7 @@ as grant the extension controller's service account the permissions it needs to resourceNames: [applications.argoproj.io, appprojects.argoproj.io, argocds.argoproj.io, argocdexports.argoproj.io, applicationsets.argoproj.io] ``` -##### Step 3. `OwnerReferencesPermissionEnforcement` permissions +#### Step 3. `OwnerReferencesPermissionEnforcement` permissions For clusters that use `OwnerReferencesPermissionEnforcement`, the installer service account must be able to update finalizers on the ClusterExtension to be able to set blockOwnerDeletion and ownerReferences for clusters that use `OwnerReferencesPermissionEnforcement`. This is only a requirement for clusters that use the [OwnerReferencesPermissionEnforcement](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement) admission plug-in. @@ -126,7 +127,7 @@ This is only a requirement for clusters that use the [OwnerReferencesPermissionE resourceNames: [argocd-operator.v0.6.0] ``` -##### Step 4. Bundled cluster-scoped resource permissions +#### Step 4. Bundled cluster-scoped resource permissions Permissions must be added for the creation and management of any cluster-scoped resources included in the bundle. In this example, the ArgoCD bundle contains a `ClusterRole` called `argocd-operator-metrics-reader`. Given that @@ -140,12 +141,13 @@ is sufficient to add the `argocd-operator-metrics-reader`resource name to the `r resourceNames: [, ..., argocd-operator-metrics-reader] ``` -##### Step 5. Operator permissions declared in the ClusterServiceVersion +#### Step 5. Operator permissions declared in the ClusterServiceVersion Include all permissions defined in the `.spec.install.permissions` ([reference](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator.v0.6.0.clusterserviceversion.yaml#L1091)) and `.spec.install.clusterPermissions` ([reference](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator.v0.6.0.clusterserviceversion.yaml#L872)) stanzas in the bundle's `ClusterServiceVersion`. These permissions are required by the extension controller, and therefore the installer service account must be able to grant them. -Note: there may be overlap between the rules defined in each stanza. Overlapping rules needn't be added twice. +!!! note + There may be overlap between the rules defined in each stanza. Overlapping rules needn't be added twice. ```yaml # from .spec.install.clusterPermissions @@ -224,12 +226,12 @@ Note: there may be overlap between the rules defined in each stanza. Overlapping # verbs: ["create", "patch"] ``` -#### Derive permissions for the installer service account `Role` +### Derive permissions for the installer service account `Role` The following steps detail how to define the namespace-scoped permissions needed by the installer service account's `Role`. The installer service account must create and manage the `RoleBinding`s for the extension controller(s). -##### Step 1. `Deployment` permissions +#### Step 1. `Deployment` permissions The installer service account must be able to create and manage the `Deployment`s for the extension controller(s). The `Deployment` name(s) can be found in the `ClusterServiceVersion` resource packed in the bundle under `.spec.install.deployments` ([reference](https://github.com/argoproj-labs/argocd-operator/blob/da6b8a7e68f71920de9545152714b9066990fc4b/deploy/olm-catalog/argocd-operator/0.6.0/argocd-operator.v0.6.0.clusterserviceversion.yaml#L1029)). @@ -246,7 +248,7 @@ This example's `ClusterServiceVersion` can be found [here](https://github.com/ar resourceNames: [argocd-operator-controller-manager] ``` -##### Step 2: `ServiceAccount` permissions +#### Step 2: `ServiceAccount` permissions The installer service account must be able to create and manage the `ServiceAccount`(s) for the extension controller(s). The `ServiceAccount` name(s) can be found in deployment template in the `ClusterServiceVersion` resource packed in the bundle under `.spec.install.deployments`. @@ -263,7 +265,7 @@ This example's `ClusterServiceVersion` can be found [here](https://github.com/ar resourceNames: [argocd-operator-controller-manager] ``` -##### Step 3. Bundled namespace-scoped resource permissions +#### Step 3. Bundled namespace-scoped resource permissions The installer service account must also create and manage other namespace-scoped resources included in the bundle. In this example, the bundle also includes two additional namespace-scoped resources: @@ -291,9 +293,10 @@ Therefore, the following permissions must be given to the installer service acco resourceNames: [argocd-operator-manager-config] ``` -#### Putting it all together +### Putting it all together Once the installer service account required cluster-scoped and namespace-scoped permissions have been collected: + 1. Create the installation namespace 2. Create the installer `ServiceAccount` 3. Create the installer `ClusterRole` @@ -304,13 +307,13 @@ Once the installer service account required cluster-scoped and namespace-scoped A manifest with the full set of resources can be found [here](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1_clusterextension.yaml). -### Alternatives +## Alternatives We understand that manually determining the minimum RBAC required for installation/upgrade of a `ClusterExtension` quite complex and protracted. In the near future, OLM v1 will provide tools and automation in order to simplify this process while maintaining our security posture. For users wishing to test out OLM v1 in a non-production settings, we offer the following alternatives: -#### Give the installer service account admin privileges +### Give the installer service account admin privileges The `cluster-admin` `ClusterRole` can be bound to the installer service account giving it full permissions to the cluster. While this obviates the need to determine the minimal RBAC required for installation, it is also dangerous. It is highly recommended @@ -344,7 +347,7 @@ kubectl create clusterrolebinding my-cluster-extension-installer-role-binding \ --serviceaccount=my-cluster-extension-namespace:my-cluster-installer-service-account ``` -#### hack/tools/catalog +### hack/tools/catalog In the spirit of making this process more tenable until the proper tools are in place, the scripts in [hack/tools/catalogs](https://github.com/operator-framework/operator-controller/blob/main/hack/tools/catalogs) were created to help the user navigate and search catalogs as well diff --git a/docs/tutorials/downgrade-extension.md b/docs/tutorials/downgrade-extension.md index e400600faf..ee25a5136e 100644 --- a/docs/tutorials/downgrade-extension.md +++ b/docs/tutorials/downgrade-extension.md @@ -51,14 +51,14 @@ spec: upgradeConstraintPolicy: SelfCertified ``` -** Disable CRD Upgrade Safety Check:** +**Command Example:** -**Patch the ClusterExtension Resource:** +If you prefer using the command line, you can use `kubectl` to modify the upgrade CRD safety check configuration. - ```bash - kubectl patch clusterextension --patch '{"spec":{"install":{"preflight":{"crdUpgradeSafety":{"policy":"Disabled"}}}}}' --type=merge - ``` - Kubernetes will apply the updated configuration, disabling CRD safety checks during the downgrade process. +```bash +kubectl patch clusterextension --patch '{"spec":{"install":{"preflight":{"crdUpgradeSafety":{"policy":"Disabled"}}}}}' --type=merge +``` +Kubernetes will apply the updated configuration, disabling CRD safety checks during the downgrade process. ### 2. Ignoring Catalog Provided Upgrade Constraints @@ -102,38 +102,39 @@ Once the CRD safety checks are disabled and upgrade constraints are set, you can 1. **Edit the ClusterExtension Resource:** - Modify the `ClusterExtension` custom resource to specify the target version and adjust the upgrade constraints. + Modify the `ClusterExtension` custom resource to specify the target version and adjust the upgrade constraints. - ```bash - kubectl edit clusterextension - ``` + ```bash + kubectl edit clusterextension + ``` 2. **Update the Version:** - Within the YAML editor, update the `spec` section as follows: - - ```yaml - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtension - metadata: - name: - spec: - source: - sourceType: Catalog - catalog: - packageName: - version: - install: - namespace: - serviceAccount: - name: - ``` - - - **`version`:** Specify the target version you wish to downgrade to. + Within the YAML editor, update the `spec` section as follows: + + ```yaml + apiVersion: olm.operatorframework.io/v1 + kind: ClusterExtension + metadata: + name: + spec: + source: + sourceType: Catalog + catalog: + packageName: + version: + install: + namespace: + serviceAccount: + name: + ``` + + `target_version` + : Specify the target version you wish to downgrade to. 3. **Apply the Changes:** - Save and exit the editor. Kubernetes will apply the changes and initiate the downgrade process. + Save and exit the editor. Kubernetes will apply the changes and initiate the downgrade process. ### 4. Post-Downgrade Verification @@ -143,31 +144,31 @@ After completing the downgrade, verify that the `ClusterExtension` is functionin 1. **Check the Status of the ClusterExtension:** - ```bash - kubectl get clusterextension -o yaml - ``` + ```bash + kubectl get clusterextension -o yaml + ``` - Ensure that the `status` reflects the target version and that there are no error messages. + Ensure that the `status` reflects the target version and that there are no error messages. 2. **Validate CRD Integrity:** - Confirm that all CRDs associated with the `ClusterExtension` are correctly installed and compatible with the downgraded version. + Confirm that all CRDs associated with the `ClusterExtension` are correctly installed and compatible with the downgraded version. - ```bash - kubectl get crd | grep - ``` + ```bash + kubectl get crd | grep + ``` 3. **Test Extension Functionality:** - Perform functional tests to ensure that the extension operates correctly in its downgraded state. + Perform functional tests to ensure that the extension operates correctly in its downgraded state. 4. **Monitor Logs:** - Check the logs of the operator managing the `ClusterExtension` for any warnings or errors. + Check the logs of the operator managing the `ClusterExtension` for any warnings or errors. - ```bash - kubectl logs deployment/ -n - ``` + ```bash + kubectl logs deployment/ -n + ``` ## Troubleshooting diff --git a/docs/tutorials/explore-available-content.md b/docs/tutorials/explore-available-content.md index 76bae2b6fd..ada0855ef3 100644 --- a/docs/tutorials/explore-available-content.md +++ b/docs/tutorials/explore-available-content.md @@ -13,8 +13,9 @@ Then you can query the catalog by using `curl` commands and the `jq` CLI tool to * You have added a ClusterCatalog of extensions, such as [OperatorHub.io](https://operatorhub.io), to your cluster. * You have installed the `jq` CLI tool. -**Note:** By default, Catalogd is installed with TLS enabled for the catalog webserver. -The following examples will show this default behavior, but for simplicity's sake will ignore TLS verification in the curl commands using the `-k` flag. +!!! note + By default, Catalogd is installed with TLS enabled for the catalog webserver. + The following examples will show this default behavior, but for simplicity's sake will ignore TLS verification in the curl commands using the `-k` flag. ## Procedure @@ -93,38 +94,38 @@ The following examples will show this default behavior, but for simplicity's sak !!! important Currently, OLM 1.0 does not support the installation of extensions that use webhooks or that target a single or specified set of namespaces. - * Return list of packages that support `AllNamespaces` install mode and do not use webhooks: +3. Return list of packages that support `AllNamespaces` install mode and do not use webhooks: - ``` terminal - curl -k https://localhost:8443/catalogs/operatorhubio/api/v1/all | jq -c 'select(.schema == "olm.bundle") | {"package":.package, "version":.properties[] | select(.type == "olm.bundle.object").value.data | @base64d | fromjson | select(.kind == "ClusterServiceVersion" and (.spec.installModes[] | select(.type == "AllNamespaces" and .supported == true) != null) and .spec.webhookdefinitions == null).spec.version}' + ``` terminal + curl -k https://localhost:8443/catalogs/operatorhubio/api/v1/all | jq -c 'select(.schema == "olm.bundle") | {"package":.package, "version":.properties[] | select(.type == "olm.bundle.object").value.data | @base64d | fromjson | select(.kind == "ClusterServiceVersion" and (.spec.installModes[] | select(.type == "AllNamespaces" and .supported == true) != null) and .spec.webhookdefinitions == null).spec.version}' + ``` + + ??? success + ``` text title="Example output" + {"package":"ack-acm-controller","version":"0.0.12"} + {"package":"ack-acmpca-controller","version":"0.0.5"} + {"package":"ack-apigatewayv2-controller","version":"1.0.7"} + {"package":"ack-applicationautoscaling-controller","version":"1.0.11"} + {"package":"ack-cloudfront-controller","version":"0.0.9"} + {"package":"ack-cloudtrail-controller","version":"1.0.8"} + {"package":"ack-cloudwatch-controller","version":"0.0.3"} + {"package":"ack-cloudwatchlogs-controller","version":"0.0.4"} + {"package":"ack-dynamodb-controller","version":"1.2.9"} + {"package":"ack-ec2-controller","version":"1.2.4"} + {"package":"ack-ecr-controller","version":"1.0.12"} + {"package":"ack-ecs-controller","version":"0.0.4"} + {"package":"ack-efs-controller","version":"0.0.5"} + {"package":"ack-eks-controller","version":"1.3.3"} + {"package":"ack-elasticache-controller","version":"0.0.29"} + {"package":"ack-emrcontainers-controller","version":"1.0.8"} + {"package":"ack-eventbridge-controller","version":"1.0.6"} + {"package":"ack-iam-controller","version":"1.3.6"} + {"package":"ack-kafka-controller","version":"0.0.4"} + {"package":"ack-keyspaces-controller","version":"0.0.11"} + ... ``` - ??? success - ``` text title="Example output" - {"package":"ack-acm-controller","version":"0.0.12"} - {"package":"ack-acmpca-controller","version":"0.0.5"} - {"package":"ack-apigatewayv2-controller","version":"1.0.7"} - {"package":"ack-applicationautoscaling-controller","version":"1.0.11"} - {"package":"ack-cloudfront-controller","version":"0.0.9"} - {"package":"ack-cloudtrail-controller","version":"1.0.8"} - {"package":"ack-cloudwatch-controller","version":"0.0.3"} - {"package":"ack-cloudwatchlogs-controller","version":"0.0.4"} - {"package":"ack-dynamodb-controller","version":"1.2.9"} - {"package":"ack-ec2-controller","version":"1.2.4"} - {"package":"ack-ecr-controller","version":"1.0.12"} - {"package":"ack-ecs-controller","version":"0.0.4"} - {"package":"ack-efs-controller","version":"0.0.5"} - {"package":"ack-eks-controller","version":"1.3.3"} - {"package":"ack-elasticache-controller","version":"0.0.29"} - {"package":"ack-emrcontainers-controller","version":"1.0.8"} - {"package":"ack-eventbridge-controller","version":"1.0.6"} - {"package":"ack-iam-controller","version":"1.3.6"} - {"package":"ack-kafka-controller","version":"0.0.4"} - {"package":"ack-keyspaces-controller","version":"0.0.11"} - ... - ``` - -3. Inspect the contents of an extension's metadata: +4. Inspect the contents of an extension's metadata: ``` terminal curl -k https://localhost:8443/catalogs/operatorhubio/api/v1/all | jq -s '.[] | select( .schema == "olm.package") | select( .name == "")' diff --git a/docs/tutorials/upgrade-extension.md b/docs/tutorials/upgrade-extension.md index 1c0e8b0616..86ecaeb75a 100644 --- a/docs/tutorials/upgrade-extension.md +++ b/docs/tutorials/upgrade-extension.md @@ -60,10 +60,10 @@ spec: EOF ``` - ??? success - ``` text title="Example output" - clusterextension.olm.operatorframework.io/argocd-operator configured - ``` + !!! success + ``` text title="Example output" + clusterextension.olm.operatorframework.io/argocd-operator configured + ``` Alternatively, you can use `kubectl patch` to update the version field: @@ -73,14 +73,14 @@ spec: `extension_name` : Specifies the name defined in the `metadata.name` field of the extension's CR. - + `target_version` : Specifies the version to upgrade or downgrade to. - ??? success - ``` text title="Example output" - clusterextension.olm.operatorframework.io/argocd-operator patched - ``` + !!! success + ``` text title="Example output" + clusterextension.olm.operatorframework.io/argocd-operator patched + ``` ### Verification @@ -91,74 +91,74 @@ spec: ``` ??? success - ``` text title="Example output" - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtension - metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"olm.operatorframework.io/v1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"argocd"},"spec":{"install":{"namespace":"argocd","serviceAccount":{"name":"argocd-installer"}},"source":{"catalog":{"packageName":"argocd-operator","version":"0.6.0"},"sourceType":"Catalog"}}} - creationTimestamp: "2024-10-03T16:02:40Z" - finalizers: - - olm.operatorframework.io/cleanup-unpack-cache - - olm.operatorframework.io/cleanup-contentmanager-cache - generation: 2 - name: argocd - resourceVersion: "1174" - uid: 0fcaf3f5-d142-4c7e-8d88-c88a549f7764 - spec: - install: - namespace: argocd - serviceAccount: - name: argocd-installer - source: - catalog: - packageName: argocd-operator - selector: {} - upgradeConstraintPolicy: CatalogProvided - version: 0.6.0 - sourceType: Catalog - status: - conditions: - - lastTransitionTime: "2024-10-03T16:02:41Z" - message: "" - observedGeneration: 2 - reason: Deprecated - status: "False" - type: Deprecated - - lastTransitionTime: "2024-10-03T16:02:41Z" - message: "" - observedGeneration: 2 - reason: Deprecated - status: "False" - type: PackageDeprecated - - lastTransitionTime: "2024-10-03T16:02:41Z" - message: "" - observedGeneration: 2 - reason: Deprecated - status: "False" - type: ChannelDeprecated - - lastTransitionTime: "2024-10-03T16:02:41Z" - message: "" - observedGeneration: 2 - reason: Deprecated - status: "False" - type: BundleDeprecated - - lastTransitionTime: "2024-10-03T16:02:43Z" - message: Installed bundle quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3 - successfully - observedGeneration: 2 - reason: Succeeded - status: "True" - type: Installed - - lastTransitionTime: "2024-10-03T16:02:43Z" - message: desired state reached - observedGeneration: 2 - reason: Succeeded - status: "False" - type: Progressing - install: - bundle: - name: argocd-operator.v0.6.0 - version: 0.6.0 - ``` + ``` text title="Example output" + apiVersion: olm.operatorframework.io/v1 + kind: ClusterExtension + metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"olm.operatorframework.io/v1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"argocd"},"spec":{"install":{"namespace":"argocd","serviceAccount":{"name":"argocd-installer"}},"source":{"catalog":{"packageName":"argocd-operator","version":"0.6.0"},"sourceType":"Catalog"}}} + creationTimestamp: "2024-10-03T16:02:40Z" + finalizers: + - olm.operatorframework.io/cleanup-unpack-cache + - olm.operatorframework.io/cleanup-contentmanager-cache + generation: 2 + name: argocd + resourceVersion: "1174" + uid: 0fcaf3f5-d142-4c7e-8d88-c88a549f7764 + spec: + install: + namespace: argocd + serviceAccount: + name: argocd-installer + source: + catalog: + packageName: argocd-operator + selector: {} + upgradeConstraintPolicy: CatalogProvided + version: 0.6.0 + sourceType: Catalog + status: + conditions: + - lastTransitionTime: "2024-10-03T16:02:41Z" + message: "" + observedGeneration: 2 + reason: Deprecated + status: "False" + type: Deprecated + - lastTransitionTime: "2024-10-03T16:02:41Z" + message: "" + observedGeneration: 2 + reason: Deprecated + status: "False" + type: PackageDeprecated + - lastTransitionTime: "2024-10-03T16:02:41Z" + message: "" + observedGeneration: 2 + reason: Deprecated + status: "False" + type: ChannelDeprecated + - lastTransitionTime: "2024-10-03T16:02:41Z" + message: "" + observedGeneration: 2 + reason: Deprecated + status: "False" + type: BundleDeprecated + - lastTransitionTime: "2024-10-03T16:02:43Z" + message: Installed bundle quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3 + successfully + observedGeneration: 2 + reason: Succeeded + status: "True" + type: Installed + - lastTransitionTime: "2024-10-03T16:02:43Z" + message: desired state reached + observedGeneration: 2 + reason: Succeeded + status: "False" + type: Progressing + install: + bundle: + name: argocd-operator.v0.6.0 + version: 0.6.0 + ``` From 6d79092c8921f888b125e084b494576e87d5706e Mon Sep 17 00:00:00 2001 From: Jordan Keister Date: Wed, 13 Nov 2024 08:16:17 -0600 Subject: [PATCH 121/694] fix typo (#1455) Signed-off-by: Jordan Keister --- docs/howto/how-to-version-range-upgrades.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/howto/how-to-version-range-upgrades.md b/docs/howto/how-to-version-range-upgrades.md index dc239fa39a..1a502a2e24 100644 --- a/docs/howto/how-to-version-range-upgrades.md +++ b/docs/howto/how-to-version-range-upgrades.md @@ -21,4 +21,4 @@ spec: name: argocd-installer ``` -For more information on SemVer version ranges see [version-rages](../concepts/version-ranges.md) \ No newline at end of file +For more information on SemVer version ranges see [version-ranges](../concepts/version-ranges.md) From 293cd7f1be56c7a6dadadeaa2027b5123b8d4374 Mon Sep 17 00:00:00 2001 From: Daniel Franz Date: Wed, 13 Nov 2024 13:52:51 -0800 Subject: [PATCH 122/694] Change from using prometheus operator to a custom test operator. (#1457) Signed-off-by: dtfranz --- hack/test/pre-upgrade-setup.sh | 3 +- test/e2e/cluster_extension_install_test.go | 45 +- .../extension_developer_test.go | 1 + test/upgrade-e2e/post_upgrade_test.go | 2 +- testdata/Dockerfile | 2 +- ...toring.coreos.com_alertmanagerconfigs.yaml | 4485 -------- .../monitoring.coreos.com_alertmanagers.yaml | 7227 ------------- .../monitoring.coreos.com_podmonitors.yaml | 684 -- .../monitoring.coreos.com_probes.yaml | 727 -- ...onitoring.coreos.com_prometheusagents.yaml | 8022 -------------- .../monitoring.coreos.com_prometheuses.yaml | 9351 ----------------- ...monitoring.coreos.com_prometheusrules.yaml | 128 - .../monitoring.coreos.com_scrapeconfigs.yaml | 215 - ...monitoring.coreos.com_servicemonitors.yaml | 714 -- .../monitoring.coreos.com_thanosrulers.yaml | 6775 ------------ .../prometheus-operator_v1_service.yaml | 20 - ...metheusoperator.clusterserviceversion.yaml | 557 - .../v1.0.0/manifests/bundle.configmap.yaml | 7 + .../olm.operatorframework.com_olme2etest.yaml | 28 + .../testoperator.clusterserviceversion.yaml | 140 + .../v1.0.0/metadata/annotations.yaml | 2 +- .../test-catalog/v1/configs/catalog.yaml | 56 +- .../test-catalog/v2/configs/catalog.yaml | 16 +- testdata/manifests/no-crds.json | 6 +- testdata/push/README.md | 4 +- 25 files changed, 250 insertions(+), 38967 deletions(-) delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagerconfigs.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagers.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_podmonitors.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_probes.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheusagents.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheuses.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheusrules.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_scrapeconfigs.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_servicemonitors.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_thanosrulers.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/prometheus-operator_v1_service.yaml delete mode 100644 testdata/images/bundles/prometheus-operator/v1.0.0/manifests/prometheusoperator.clusterserviceversion.yaml create mode 100644 testdata/images/bundles/test-operator/v1.0.0/manifests/bundle.configmap.yaml create mode 100644 testdata/images/bundles/test-operator/v1.0.0/manifests/olm.operatorframework.com_olme2etest.yaml create mode 100644 testdata/images/bundles/test-operator/v1.0.0/manifests/testoperator.clusterserviceversion.yaml rename testdata/images/bundles/{prometheus-operator => test-operator}/v1.0.0/metadata/annotations.yaml (88%) diff --git a/hack/test/pre-upgrade-setup.sh b/hack/test/pre-upgrade-setup.sh index 33dd035fbd..f8c3fee95e 100755 --- a/hack/test/pre-upgrade-setup.sh +++ b/hack/test/pre-upgrade-setup.sh @@ -49,6 +49,7 @@ rules: - apiGroups: - "" resources: + - "configmaps" - "secrets" - "services" - "serviceaccounts" @@ -138,7 +139,7 @@ spec: source: sourceType: Catalog catalog: - packageName: prometheus + packageName: test version: 1.0.0 EOF diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index cecb7f2191..6d137fb1a8 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -91,6 +91,7 @@ func createClusterRoleAndBindingForSA(ctx context.Context, name string, sa *core "", }, Resources: []string{ + "configmaps", "secrets", // for helm "services", "serviceaccounts", @@ -289,7 +290,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { for _, tc := range []testCase{ { name: "no registry configuration necessary", - packageName: "prometheus", + packageName: "test", }, { // NOTE: This test requires an extra configuration in /etc/containers/registries.conf, which is mounted @@ -297,7 +298,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { // The goal here is to prove that "mirrored-registry.operator-controller-e2e.svc.cluster.local:5000" is // mapped to the "real" registry hostname ("docker-registry.operator-controller-e2e.svc.cluster.local:5000"). name: "package requires mirror registry configuration in /etc/containers/registries.conf", - packageName: "prometheus-mirrored", + packageName: "test-mirrored", }, } { t.Run(tc.name, func(t *testing.T) { @@ -361,14 +362,24 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { t.Log("When a cluster extension is installed from a catalog") clusterExtension, extensionCatalog, sa, ns := testInit(t) + extraCatalog, err := createTestCatalog(context.Background(), "extra-test-catalog", os.Getenv(testCatalogRefEnvVar)) + require.NoError(t, err) + defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns) defer getArtifactsOutput(t) + defer func(cat *catalogd.ClusterCatalog) { + require.NoError(t, c.Delete(context.Background(), cat)) + require.Eventually(t, func() bool { + err := c.Get(context.Background(), types.NamespacedName{Name: cat.Name}, &catalogd.ClusterCatalog{}) + return errors.IsNotFound(err) + }, pollDuration, pollInterval) + }(extraCatalog) clusterExtension.Spec = ocv1.ClusterExtensionSpec{ Source: ocv1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1.CatalogSource{ - PackageName: "prometheus", + PackageName: "test", }, }, Namespace: ns.Name, @@ -392,7 +403,7 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) { if assert.NotNil(ct, cond) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1.ReasonRetrying, cond.Reason) - assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [operatorhubio test-catalog]") + assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [extra-test-catalog test-catalog]") } }, pollDuration, pollInterval) } @@ -410,7 +421,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { Source: ocv1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1.CatalogSource{ - PackageName: "prometheus", + PackageName: "test", Version: "1.0.0", // No Selector since this is an exact version match }, @@ -426,7 +437,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) assert.Equal(ct, &ocv1.ClusterExtensionInstallStatus{Bundle: ocv1.BundleMetadata{ - Name: "prometheus-operator.1.0.0", + Name: "test-operator.1.0.0", Version: "1.0.0", }}, clusterExtension.Status.Install, @@ -455,7 +466,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) if assert.NotNil(ct, cond) { assert.Equal(ct, ocv1.ReasonRetrying, cond.Reason) - assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"prometheus\" matching version \"1.2.0\"", cond.Message) + assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"test\" matching version \"1.2.0\"", cond.Message) } }, pollDuration, pollInterval) } @@ -473,7 +484,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { Source: ocv1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1.CatalogSource{ - PackageName: "prometheus", + PackageName: "test", Version: "1.0.0", }, }, @@ -522,7 +533,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { Source: ocv1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1.CatalogSource{ - PackageName: "prometheus", + PackageName: "test", Version: "1.0.0", }, }, @@ -569,7 +580,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { Source: ocv1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1.CatalogSource{ - PackageName: "prometheus", + PackageName: "test", Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { @@ -656,7 +667,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { Source: ocv1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1.CatalogSource{ - PackageName: "prometheus", + PackageName: "test", Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, @@ -717,7 +728,7 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T Source: ocv1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1.CatalogSource{ - PackageName: "prometheus", + PackageName: "test", Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, @@ -744,17 +755,17 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T }, pollDuration, pollInterval) t.Log("By deleting a managed resource") - prometheusService := &corev1.Service{ + testConfigMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: "prometheus-operator", + Name: "test-configmap", Namespace: clusterExtension.Spec.Namespace, }, } - require.NoError(t, c.Delete(context.Background(), prometheusService)) + require.NoError(t, c.Delete(context.Background(), testConfigMap)) t.Log("By eventually re-creating the managed resource") require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: prometheusService.Name, Namespace: prometheusService.Namespace}, prometheusService)) + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: testConfigMap.Name, Namespace: testConfigMap.Namespace}, testConfigMap)) }, pollDuration, pollInterval) } @@ -780,7 +791,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes Source: ocv1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1.CatalogSource{ - PackageName: "prometheus", + PackageName: "test", Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, diff --git a/test/extension-developer-e2e/extension_developer_test.go b/test/extension-developer-e2e/extension_developer_test.go index 6974983a0e..5edaa910cd 100644 --- a/test/extension-developer-e2e/extension_developer_test.go +++ b/test/extension-developer-e2e/extension_developer_test.go @@ -104,6 +104,7 @@ func TestExtensionDeveloper(t *testing.T) { "", }, Resources: []string{ + "configmaps", "services", "serviceaccounts", }, diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go index ace977d130..e361f88144 100644 --- a/test/upgrade-e2e/post_upgrade_test.go +++ b/test/upgrade-e2e/post_upgrade_test.go @@ -107,7 +107,7 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) { } assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) assert.Contains(ct, cond.Message, "Installed bundle") - assert.Equal(ct, ocv1.BundleMetadata{Name: "prometheus-operator.1.0.1", Version: "1.0.1"}, clusterExtension.Status.Install.Bundle) + assert.Equal(ct, ocv1.BundleMetadata{Name: "test-operator.1.0.1", Version: "1.0.1"}, clusterExtension.Status.Install.Bundle) assert.NotEqual(ct, previousVersion, clusterExtension.Status.Install.Bundle.Version) }, time.Minute, time.Second) } diff --git a/testdata/Dockerfile b/testdata/Dockerfile index 0f1355f56d..2868542e61 100644 --- a/testdata/Dockerfile +++ b/testdata/Dockerfile @@ -1,4 +1,4 @@ -from gcr.io/distroless/static:nonroot +FROM gcr.io/distroless/static:nonroot WORKDIR / diff --git a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagerconfigs.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagerconfigs.yaml deleted file mode 100644 index 64919a78a1..0000000000 --- a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagerconfigs.yaml +++ /dev/null @@ -1,4485 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.11.1 - creationTimestamp: null - name: alertmanagerconfigs.monitoring.coreos.com -spec: - group: monitoring.coreos.com - names: - categories: - - prometheus-operator - kind: AlertmanagerConfig - listKind: AlertmanagerConfigList - plural: alertmanagerconfigs - shortNames: - - amcfg - singular: alertmanagerconfig - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: AlertmanagerConfig defines a namespaced AlertmanagerConfig to - be aggregated across multiple namespaces configuring one Alertmanager cluster. - 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: AlertmanagerConfigSpec is a specification of the desired - behavior of the Alertmanager configuration. By definition, the Alertmanager - configuration only applies to alerts for which the `namespace` label - is equal to the namespace of the AlertmanagerConfig resource. - properties: - inhibitRules: - description: List of inhibition rules. The rules will only apply to - alerts matching the resource's namespace. - items: - description: InhibitRule defines an inhibition rule that allows - to mute alerts when other alerts are already firing. See https://prometheus.io/docs/alerting/latest/configuration/#inhibit_rule - properties: - equal: - description: Labels that must have an equal value in the source - and target alert for the inhibition to take effect. - items: - type: string - type: array - sourceMatch: - description: Matchers for which one or more alerts have to exist - for the inhibition to take effect. The operator enforces that - the alert matches the resource's namespace. - items: - description: Matcher defines how to match on alert's labels. - properties: - matchType: - description: Match operation available with AlertManager - >= v0.22.0 and takes precedence over Regex (deprecated) - if non-empty. - enum: - - '!=' - - = - - =~ - - '!~' - type: string - name: - description: Label to match. - minLength: 1 - type: string - regex: - description: Whether to match on equality (false) or regular-expression - (true). Deprecated as of AlertManager >= v0.22.0 where - a user should use MatchType instead. - type: boolean - value: - description: Label value to match. - type: string - required: - - name - type: object - type: array - targetMatch: - description: Matchers that have to be fulfilled in the alerts - to be muted. The operator enforces that the alert matches - the resource's namespace. - items: - description: Matcher defines how to match on alert's labels. - properties: - matchType: - description: Match operation available with AlertManager - >= v0.22.0 and takes precedence over Regex (deprecated) - if non-empty. - enum: - - '!=' - - = - - =~ - - '!~' - type: string - name: - description: Label to match. - minLength: 1 - type: string - regex: - description: Whether to match on equality (false) or regular-expression - (true). Deprecated as of AlertManager >= v0.22.0 where - a user should use MatchType instead. - type: boolean - value: - description: Label value to match. - type: string - required: - - name - type: object - type: array - type: object - type: array - muteTimeIntervals: - description: List of MuteTimeInterval specifying when the routes should - be muted. - items: - description: MuteTimeInterval specifies the periods in time when - notifications will be muted - properties: - name: - description: Name of the time interval - type: string - timeIntervals: - description: TimeIntervals is a list of TimeInterval - items: - description: TimeInterval describes intervals of time - properties: - daysOfMonth: - description: DaysOfMonth is a list of DayOfMonthRange - items: - description: DayOfMonthRange is an inclusive range of - days of the month beginning at 1 - properties: - end: - description: End of the inclusive range - maximum: 31 - minimum: -31 - type: integer - start: - description: Start of the inclusive range - maximum: 31 - minimum: -31 - type: integer - type: object - type: array - months: - description: Months is a list of MonthRange - items: - description: MonthRange is an inclusive range of months - of the year beginning in January Months can be specified - by name (e.g 'January') by numerical month (e.g '1') - or as an inclusive range (e.g 'January:March', '1:3', - '1:March') - pattern: ^((?i)january|february|march|april|may|june|july|august|september|october|november|december|[1-12])(?:((:((?i)january|february|march|april|may|june|july|august|september|october|november|december|[1-12]))$)|$) - type: string - type: array - times: - description: Times is a list of TimeRange - items: - description: TimeRange defines a start and end time - in 24hr format - properties: - endTime: - description: EndTime is the end time in 24hr format. - pattern: ^((([01][0-9])|(2[0-3])):[0-5][0-9])$|(^24:00$) - type: string - startTime: - description: StartTime is the start time in 24hr - format. - pattern: ^((([01][0-9])|(2[0-3])):[0-5][0-9])$|(^24:00$) - type: string - type: object - type: array - weekdays: - description: Weekdays is a list of WeekdayRange - items: - description: WeekdayRange is an inclusive range of days - of the week beginning on Sunday Days can be specified - by name (e.g 'Sunday') or as an inclusive range (e.g - 'Monday:Friday') - pattern: ^((?i)sun|mon|tues|wednes|thurs|fri|satur)day(?:((:(sun|mon|tues|wednes|thurs|fri|satur)day)$)|$) - type: string - type: array - years: - description: Years is a list of YearRange - items: - description: YearRange is an inclusive range of years - pattern: ^2\d{3}(?::2\d{3}|$) - type: string - type: array - type: object - type: array - type: object - type: array - receivers: - description: List of receivers. - items: - description: Receiver defines one or more notification integrations. - properties: - emailConfigs: - description: List of Email configurations. - items: - description: EmailConfig configures notifications via Email. - properties: - authIdentity: - description: The identity to use for authentication. - type: string - authPassword: - description: The secret's key that contains the password - to use for authentication. The secret needs to be in - the same namespace as the AlertmanagerConfig object - and accessible by the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - authSecret: - description: The secret's key that contains the CRAM-MD5 - secret. The secret needs to be in the same namespace - as the AlertmanagerConfig object and accessible by the - Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - authUsername: - description: The username to use for authentication. - type: string - from: - description: The sender address. - type: string - headers: - description: Further headers email header key/value pairs. - Overrides any headers previously set by the notification - implementation. - items: - description: KeyValue defines a (key, value) tuple. - properties: - key: - description: Key of the tuple. - minLength: 1 - type: string - value: - description: Value of the tuple. - type: string - required: - - key - - value - type: object - type: array - hello: - description: The hostname to identify to the SMTP server. - type: string - html: - description: The HTML body of the email notification. - type: string - requireTLS: - description: The SMTP TLS requirement. Note that Go does - not support unencrypted connections to remote SMTP endpoints. - type: boolean - sendResolved: - description: Whether or not to notify about resolved alerts. - type: boolean - smarthost: - description: The SMTP host and port through which emails - are sent. E.g. example.com:25 - type: string - text: - description: The text body of the email notification. - type: string - tlsConfig: - description: TLS configuration - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to use - for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for - the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when doing - client-authentication. - properties: - configMap: - description: ConfigMap containing data to use - for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for - the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key file - for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the targets. - type: string - type: object - to: - description: The email address to send notifications to. - type: string - type: object - type: array - name: - description: Name of the receiver. Must be unique across all - items from the list. - minLength: 1 - type: string - opsgenieConfigs: - description: List of OpsGenie configurations. - items: - description: OpsGenieConfig configures notifications via OpsGenie. - See https://prometheus.io/docs/alerting/latest/configuration/#opsgenie_config - properties: - actions: - description: Comma separated list of actions that will - be available for the alert. - type: string - apiKey: - description: The secret's key that contains the OpsGenie - API key. The secret needs to be in the same namespace - as the AlertmanagerConfig object and accessible by the - Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - apiURL: - description: The URL to send OpsGenie API requests to. - type: string - description: - description: Description of the incident. - type: string - details: - description: A set of arbitrary key/value pairs that provide - further detail about the incident. - items: - description: KeyValue defines a (key, value) tuple. - properties: - key: - description: Key of the tuple. - minLength: 1 - type: string - value: - description: Value of the tuple. - type: string - required: - - key - - value - type: object - type: array - entity: - description: Optional field that can be used to specify - which domain alert is related to. - type: string - httpConfig: - description: HTTP client configuration. - properties: - authorization: - description: Authorization header configuration for - the client. This is mutually exclusive with BasicAuth - and is only available starting from Alertmanager - v0.22+. - properties: - credentials: - description: The secret's key that contains the - credentials of the request - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, - BasicAuth takes precedence. - properties: - password: - description: The secret in the service monitor - namespace that contains the password for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor - namespace that contains the username for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. - The secret needs to be in the same namespace as - the AlertmanagerConfig object and accessible by - the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - followRedirects: - description: FollowRedirects specifies whether the - client should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch - a token for the targets. - properties: - clientId: - description: The secret or configmap containing - the OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 - client secret - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token - URL - type: object - scopes: - description: OAuth2 scopes used for the token - request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when - doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key - file for the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the - targets. - type: string - type: object - type: object - message: - description: Alert text limited to 130 characters. - type: string - note: - description: Additional alert note. - type: string - priority: - description: Priority level of alert. Possible values - are P1, P2, P3, P4, and P5. - type: string - responders: - description: List of responders responsible for notifications. - items: - description: OpsGenieConfigResponder defines a responder - to an incident. One of `id`, `name` or `username` - has to be defined. - properties: - id: - description: ID of the responder. - type: string - name: - description: Name of the responder. - type: string - type: - description: Type of responder. - enum: - - team - - teams - - user - - escalation - - schedule - minLength: 1 - type: string - username: - description: Username of the responder. - type: string - required: - - type - type: object - type: array - sendResolved: - description: Whether or not to notify about resolved alerts. - type: boolean - source: - description: Backlink to the sender of the notification. - type: string - tags: - description: Comma separated list of tags attached to - the notifications. - type: string - updateAlerts: - description: Whether to update message and description - of the alert in OpsGenie if it already exists By default, - the alert is never updated in OpsGenie, the new message - only appears in activity log. - type: boolean - type: object - type: array - pagerdutyConfigs: - description: List of PagerDuty configurations. - items: - description: PagerDutyConfig configures notifications via - PagerDuty. See https://prometheus.io/docs/alerting/latest/configuration/#pagerduty_config - properties: - class: - description: The class/type of the event. - type: string - client: - description: Client identification. - type: string - clientURL: - description: Backlink to the sender of notification. - type: string - component: - description: The part or component of the affected system - that is broken. - type: string - description: - description: Description of the incident. - type: string - details: - description: Arbitrary key/value pairs that provide further - detail about the incident. - items: - description: KeyValue defines a (key, value) tuple. - properties: - key: - description: Key of the tuple. - minLength: 1 - type: string - value: - description: Value of the tuple. - type: string - required: - - key - - value - type: object - type: array - group: - description: A cluster or grouping of sources. - type: string - httpConfig: - description: HTTP client configuration. - properties: - authorization: - description: Authorization header configuration for - the client. This is mutually exclusive with BasicAuth - and is only available starting from Alertmanager - v0.22+. - properties: - credentials: - description: The secret's key that contains the - credentials of the request - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, - BasicAuth takes precedence. - properties: - password: - description: The secret in the service monitor - namespace that contains the password for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor - namespace that contains the username for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. - The secret needs to be in the same namespace as - the AlertmanagerConfig object and accessible by - the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - followRedirects: - description: FollowRedirects specifies whether the - client should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch - a token for the targets. - properties: - clientId: - description: The secret or configmap containing - the OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 - client secret - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token - URL - type: object - scopes: - description: OAuth2 scopes used for the token - request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when - doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key - file for the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the - targets. - type: string - type: object - type: object - pagerDutyImageConfigs: - description: A list of image details to attach that provide - further detail about an incident. - items: - description: PagerDutyImageConfig attaches images to - an incident - properties: - alt: - description: Alt is the optional alternative text - for the image. - type: string - href: - description: Optional URL; makes the image a clickable - link. - type: string - src: - description: Src of the image being attached to - the incident - type: string - type: object - type: array - pagerDutyLinkConfigs: - description: A list of link details to attach that provide - further detail about an incident. - items: - description: PagerDutyLinkConfig attaches text links - to an incident - properties: - alt: - description: Text that describes the purpose of - the link, and can be used as the link's text. - type: string - href: - description: Href is the URL of the link to be attached - type: string - type: object - type: array - routingKey: - description: The secret's key that contains the PagerDuty - integration key (when using Events API v2). Either this - field or `serviceKey` needs to be defined. The secret - needs to be in the same namespace as the AlertmanagerConfig - object and accessible by the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - sendResolved: - description: Whether or not to notify about resolved alerts. - type: boolean - serviceKey: - description: The secret's key that contains the PagerDuty - service key (when using integration type "Prometheus"). - Either this field or `routingKey` needs to be defined. - The secret needs to be in the same namespace as the - AlertmanagerConfig object and accessible by the Prometheus - Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - severity: - description: Severity of the incident. - type: string - url: - description: The URL to send requests to. - type: string - type: object - type: array - pushoverConfigs: - description: List of Pushover configurations. - items: - description: PushoverConfig configures notifications via Pushover. - See https://prometheus.io/docs/alerting/latest/configuration/#pushover_config - properties: - expire: - description: How long your notification will continue - to be retried for, unless the user acknowledges the - notification. - pattern: ^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$ - type: string - html: - description: Whether notification message is HTML or plain - text. - type: boolean - httpConfig: - description: HTTP client configuration. - properties: - authorization: - description: Authorization header configuration for - the client. This is mutually exclusive with BasicAuth - and is only available starting from Alertmanager - v0.22+. - properties: - credentials: - description: The secret's key that contains the - credentials of the request - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, - BasicAuth takes precedence. - properties: - password: - description: The secret in the service monitor - namespace that contains the password for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor - namespace that contains the username for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. - The secret needs to be in the same namespace as - the AlertmanagerConfig object and accessible by - the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - followRedirects: - description: FollowRedirects specifies whether the - client should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch - a token for the targets. - properties: - clientId: - description: The secret or configmap containing - the OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 - client secret - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token - URL - type: object - scopes: - description: OAuth2 scopes used for the token - request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when - doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key - file for the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the - targets. - type: string - type: object - type: object - message: - description: Notification message. - type: string - priority: - description: Priority, see https://pushover.net/api#priority - type: string - retry: - description: How often the Pushover servers will send - the same notification to the user. Must be at least - 30 seconds. - pattern: ^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$ - type: string - sendResolved: - description: Whether or not to notify about resolved alerts. - type: boolean - sound: - description: The name of one of the sounds supported by - device clients to override the user's default sound - choice - type: string - title: - description: Notification title. - type: string - token: - description: The secret's key that contains the registered - application's API token, see https://pushover.net/apps. - The secret needs to be in the same namespace as the - AlertmanagerConfig object and accessible by the Prometheus - Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - url: - description: A supplementary URL shown alongside the message. - type: string - urlTitle: - description: A title for supplementary URL, otherwise - just the URL is shown - type: string - userKey: - description: The secret's key that contains the recipient - user's user key. The secret needs to be in the same - namespace as the AlertmanagerConfig object and accessible - by the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - type: object - type: array - slackConfigs: - description: List of Slack configurations. - items: - description: SlackConfig configures notifications via Slack. - See https://prometheus.io/docs/alerting/latest/configuration/#slack_config - properties: - actions: - description: A list of Slack actions that are sent with - each notification. - items: - description: SlackAction configures a single Slack action - that is sent with each notification. See https://api.slack.com/docs/message-attachments#action_fields - and https://api.slack.com/docs/message-buttons for - more information. - properties: - confirm: - description: SlackConfirmationField protect users - from destructive actions or particularly distinguished - decisions by asking them to confirm their button - click one more time. See https://api.slack.com/docs/interactive-message-field-guide#confirmation_fields - for more information. - properties: - dismissText: - type: string - okText: - type: string - text: - minLength: 1 - type: string - title: - type: string - required: - - text - type: object - name: - type: string - style: - type: string - text: - minLength: 1 - type: string - type: - minLength: 1 - type: string - url: - type: string - value: - type: string - required: - - text - - type - type: object - type: array - apiURL: - description: The secret's key that contains the Slack - webhook URL. The secret needs to be in the same namespace - as the AlertmanagerConfig object and accessible by the - Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - callbackId: - type: string - channel: - description: The channel or user to send notifications - to. - type: string - color: - type: string - fallback: - type: string - fields: - description: A list of Slack fields that are sent with - each notification. - items: - description: SlackField configures a single Slack field - that is sent with each notification. Each field must - contain a title, value, and optionally, a boolean - value to indicate if the field is short enough to - be displayed next to other fields designated as short. - See https://api.slack.com/docs/message-attachments#fields - for more information. - properties: - short: - type: boolean - title: - minLength: 1 - type: string - value: - minLength: 1 - type: string - required: - - title - - value - type: object - type: array - footer: - type: string - httpConfig: - description: HTTP client configuration. - properties: - authorization: - description: Authorization header configuration for - the client. This is mutually exclusive with BasicAuth - and is only available starting from Alertmanager - v0.22+. - properties: - credentials: - description: The secret's key that contains the - credentials of the request - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, - BasicAuth takes precedence. - properties: - password: - description: The secret in the service monitor - namespace that contains the password for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor - namespace that contains the username for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. - The secret needs to be in the same namespace as - the AlertmanagerConfig object and accessible by - the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - followRedirects: - description: FollowRedirects specifies whether the - client should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch - a token for the targets. - properties: - clientId: - description: The secret or configmap containing - the OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 - client secret - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token - URL - type: object - scopes: - description: OAuth2 scopes used for the token - request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when - doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key - file for the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the - targets. - type: string - type: object - type: object - iconEmoji: - type: string - iconURL: - type: string - imageURL: - type: string - linkNames: - type: boolean - mrkdwnIn: - items: - type: string - type: array - pretext: - type: string - sendResolved: - description: Whether or not to notify about resolved alerts. - type: boolean - shortFields: - type: boolean - text: - type: string - thumbURL: - type: string - title: - type: string - titleLink: - type: string - username: - type: string - type: object - type: array - snsConfigs: - description: List of SNS configurations - items: - description: SNSConfig configures notifications via AWS SNS. - See https://prometheus.io/docs/alerting/latest/configuration/#sns_configs - properties: - apiURL: - description: The SNS API URL i.e. https://sns.us-east-2.amazonaws.com. - If not specified, the SNS API URL from the SNS SDK will - be used. - type: string - attributes: - additionalProperties: - type: string - description: SNS message attributes. - type: object - httpConfig: - description: HTTP client configuration. - properties: - authorization: - description: Authorization header configuration for - the client. This is mutually exclusive with BasicAuth - and is only available starting from Alertmanager - v0.22+. - properties: - credentials: - description: The secret's key that contains the - credentials of the request - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, - BasicAuth takes precedence. - properties: - password: - description: The secret in the service monitor - namespace that contains the password for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor - namespace that contains the username for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. - The secret needs to be in the same namespace as - the AlertmanagerConfig object and accessible by - the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - followRedirects: - description: FollowRedirects specifies whether the - client should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch - a token for the targets. - properties: - clientId: - description: The secret or configmap containing - the OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 - client secret - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token - URL - type: object - scopes: - description: OAuth2 scopes used for the token - request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when - doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key - file for the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the - targets. - type: string - type: object - type: object - message: - description: The message content of the SNS notification. - type: string - phoneNumber: - description: Phone number if message is delivered via - SMS in E.164 format. If you don't specify this value, - you must specify a value for the TopicARN or TargetARN. - type: string - sendResolved: - description: Whether or not to notify about resolved alerts. - type: boolean - sigv4: - description: Configures AWS's Signature Verification 4 - signing process to sign requests. - properties: - accessKey: - description: AccessKey is the AWS API key. If blank, - the environment variable `AWS_ACCESS_KEY_ID` is - used. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - profile: - description: Profile is the named AWS profile used - to authenticate. - type: string - region: - description: Region is the AWS region. If blank, the - region from the default credentials chain used. - type: string - roleArn: - description: RoleArn is the named AWS profile used - to authenticate. - type: string - secretKey: - description: SecretKey is the AWS API secret. If blank, - the environment variable `AWS_SECRET_ACCESS_KEY` - is used. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - subject: - description: Subject line when the message is delivered - to email endpoints. - type: string - targetARN: - description: The mobile platform endpoint ARN if message - is delivered via mobile notifications. If you don't - specify this value, you must specify a value for the - topic_arn or PhoneNumber. - type: string - topicARN: - description: SNS topic ARN, i.e. arn:aws:sns:us-east-2:698519295917:My-Topic - If you don't specify this value, you must specify a - value for the PhoneNumber or TargetARN. - type: string - type: object - type: array - telegramConfigs: - description: List of Telegram configurations. - items: - description: TelegramConfig configures notifications via Telegram. - See https://prometheus.io/docs/alerting/latest/configuration/#telegram_config - properties: - apiURL: - description: The Telegram API URL i.e. https://api.telegram.org. - If not specified, default API URL will be used. - type: string - botToken: - description: Telegram bot token The secret needs to be - in the same namespace as the AlertmanagerConfig object - and accessible by the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - chatID: - description: The Telegram chat ID. - format: int64 - type: integer - disableNotifications: - description: Disable telegram notifications - type: boolean - httpConfig: - description: HTTP client configuration. - properties: - authorization: - description: Authorization header configuration for - the client. This is mutually exclusive with BasicAuth - and is only available starting from Alertmanager - v0.22+. - properties: - credentials: - description: The secret's key that contains the - credentials of the request - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, - BasicAuth takes precedence. - properties: - password: - description: The secret in the service monitor - namespace that contains the password for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor - namespace that contains the username for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. - The secret needs to be in the same namespace as - the AlertmanagerConfig object and accessible by - the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - followRedirects: - description: FollowRedirects specifies whether the - client should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch - a token for the targets. - properties: - clientId: - description: The secret or configmap containing - the OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 - client secret - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token - URL - type: object - scopes: - description: OAuth2 scopes used for the token - request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when - doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key - file for the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the - targets. - type: string - type: object - type: object - message: - description: Message template - type: string - parseMode: - description: Parse mode for telegram message - enum: - - MarkdownV2 - - Markdown - - HTML - type: string - sendResolved: - description: Whether to notify about resolved alerts. - type: boolean - type: object - type: array - victoropsConfigs: - description: List of VictorOps configurations. - items: - description: VictorOpsConfig configures notifications via - VictorOps. See https://prometheus.io/docs/alerting/latest/configuration/#victorops_config - properties: - apiKey: - description: The secret's key that contains the API key - to use when talking to the VictorOps API. The secret - needs to be in the same namespace as the AlertmanagerConfig - object and accessible by the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - apiUrl: - description: The VictorOps API URL. - type: string - customFields: - description: Additional custom fields for notification. - items: - description: KeyValue defines a (key, value) tuple. - properties: - key: - description: Key of the tuple. - minLength: 1 - type: string - value: - description: Value of the tuple. - type: string - required: - - key - - value - type: object - type: array - entityDisplayName: - description: Contains summary of the alerted problem. - type: string - httpConfig: - description: The HTTP client's configuration. - properties: - authorization: - description: Authorization header configuration for - the client. This is mutually exclusive with BasicAuth - and is only available starting from Alertmanager - v0.22+. - properties: - credentials: - description: The secret's key that contains the - credentials of the request - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, - BasicAuth takes precedence. - properties: - password: - description: The secret in the service monitor - namespace that contains the password for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor - namespace that contains the username for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. - The secret needs to be in the same namespace as - the AlertmanagerConfig object and accessible by - the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - followRedirects: - description: FollowRedirects specifies whether the - client should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch - a token for the targets. - properties: - clientId: - description: The secret or configmap containing - the OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 - client secret - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token - URL - type: object - scopes: - description: OAuth2 scopes used for the token - request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when - doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key - file for the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the - targets. - type: string - type: object - type: object - messageType: - description: Describes the behavior of the alert (CRITICAL, - WARNING, INFO). - type: string - monitoringTool: - description: The monitoring tool the state message is - from. - type: string - routingKey: - description: A key used to map the alert to a team. - type: string - sendResolved: - description: Whether or not to notify about resolved alerts. - type: boolean - stateMessage: - description: Contains long explanation of the alerted - problem. - type: string - type: object - type: array - webhookConfigs: - description: List of webhook configurations. - items: - description: WebhookConfig configures notifications via a - generic receiver supporting the webhook payload. See https://prometheus.io/docs/alerting/latest/configuration/#webhook_config - properties: - httpConfig: - description: HTTP client configuration. - properties: - authorization: - description: Authorization header configuration for - the client. This is mutually exclusive with BasicAuth - and is only available starting from Alertmanager - v0.22+. - properties: - credentials: - description: The secret's key that contains the - credentials of the request - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, - BasicAuth takes precedence. - properties: - password: - description: The secret in the service monitor - namespace that contains the password for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor - namespace that contains the username for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. - The secret needs to be in the same namespace as - the AlertmanagerConfig object and accessible by - the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - followRedirects: - description: FollowRedirects specifies whether the - client should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch - a token for the targets. - properties: - clientId: - description: The secret or configmap containing - the OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 - client secret - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token - URL - type: object - scopes: - description: OAuth2 scopes used for the token - request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when - doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key - file for the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the - targets. - type: string - type: object - type: object - maxAlerts: - description: Maximum number of alerts to be sent per webhook - message. When 0, all alerts are included. - format: int32 - minimum: 0 - type: integer - sendResolved: - description: Whether or not to notify about resolved alerts. - type: boolean - url: - description: The URL to send HTTP POST requests to. `urlSecret` - takes precedence over `url`. One of `urlSecret` and - `url` should be defined. - type: string - urlSecret: - description: The secret's key that contains the webhook - URL to send HTTP requests to. `urlSecret` takes precedence - over `url`. One of `urlSecret` and `url` should be defined. - The secret needs to be in the same namespace as the - AlertmanagerConfig object and accessible by the Prometheus - Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - type: object - type: array - wechatConfigs: - description: List of WeChat configurations. - items: - description: WeChatConfig configures notifications via WeChat. - See https://prometheus.io/docs/alerting/latest/configuration/#wechat_config - properties: - agentID: - type: string - apiSecret: - description: The secret's key that contains the WeChat - API key. The secret needs to be in the same namespace - as the AlertmanagerConfig object and accessible by the - Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - apiURL: - description: The WeChat API URL. - type: string - corpID: - description: The corp id for authentication. - type: string - httpConfig: - description: HTTP client configuration. - properties: - authorization: - description: Authorization header configuration for - the client. This is mutually exclusive with BasicAuth - and is only available starting from Alertmanager - v0.22+. - properties: - credentials: - description: The secret's key that contains the - credentials of the request - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, - BasicAuth takes precedence. - properties: - password: - description: The secret in the service monitor - namespace that contains the password for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor - namespace that contains the username for authentication. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. - The secret needs to be in the same namespace as - the AlertmanagerConfig object and accessible by - the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - followRedirects: - description: FollowRedirects specifies whether the - client should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch - a token for the targets. - properties: - clientId: - description: The secret or configmap containing - the OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 - client secret - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token - URL - type: object - scopes: - description: OAuth2 scopes used for the token - request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when - doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to - use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use - for the targets. - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key - file for the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the - targets. - type: string - type: object - type: object - message: - description: API request data as defined by the WeChat - API. - type: string - messageType: - type: string - sendResolved: - description: Whether or not to notify about resolved alerts. - type: boolean - toParty: - type: string - toTag: - type: string - toUser: - type: string - type: object - type: array - required: - - name - type: object - type: array - route: - description: The Alertmanager route definition for alerts matching - the resource's namespace. If present, it will be added to the generated - Alertmanager configuration as a first-level route. - properties: - activeTimeIntervals: - description: ActiveTimeIntervals is a list of MuteTimeInterval - names when this route should be active. - items: - type: string - type: array - continue: - description: Boolean indicating whether an alert should continue - matching subsequent sibling nodes. It will always be overridden - to true for the first-level route by the Prometheus operator. - type: boolean - groupBy: - description: List of labels to group by. Labels must not be repeated - (unique list). Special label "..." (aggregate by all possible - labels), if provided, must be the only element in the list. - items: - type: string - type: array - groupInterval: - description: 'How long to wait before sending an updated notification. - Must match the regular expression`^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$` - Example: "5m"' - type: string - groupWait: - description: 'How long to wait before sending the initial notification. - Must match the regular expression`^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$` - Example: "30s"' - type: string - matchers: - description: 'List of matchers that the alert''s labels should - match. For the first level route, the operator removes any existing - equality and regexp matcher on the `namespace` label and adds - a `namespace: ` matcher.' - items: - description: Matcher defines how to match on alert's labels. - properties: - matchType: - description: 'Match operation available with AlertManager - >= v0.22.0 and takes precedence over Regex (deprecated) - if non-empty.' - enum: - - '!=' - - = - - =~ - - '!~' - type: string - name: - description: Label to match. - minLength: 1 - type: string - regex: - description: Whether to match on equality (false) or regular-expression - (true). Deprecated as of AlertManager >= v0.22.0 where - a user should use MatchType instead. - type: boolean - value: - description: Label value to match. - type: string - required: - - name - type: object - type: array - muteTimeIntervals: - description: 'Note: this comment applies to the field definition - above but appears below otherwise it gets included in the generated - manifest. CRD schema doesn''t support self-referential types - for now (see https://github.com/kubernetes/kubernetes/issues/62872). - We have to use an alternative type to circumvent the limitation. - The downside is that the Kube API can''t validate the data beyond - the fact that it is a valid JSON representation. MuteTimeIntervals - is a list of MuteTimeInterval names that will mute this route - when matched,' - items: - type: string - type: array - receiver: - description: Name of the receiver for this route. If not empty, - it should be listed in the `receivers` field. - type: string - repeatInterval: - description: 'How long to wait before repeating the last notification. - Must match the regular expression`^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$` - Example: "4h"' - type: string - routes: - description: Child routes. - items: - x-kubernetes-preserve-unknown-fields: true - type: array - type: object - type: object - required: - - spec - type: object - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null diff --git a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagers.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagers.yaml deleted file mode 100644 index 526ad188fb..0000000000 --- a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_alertmanagers.yaml +++ /dev/null @@ -1,7227 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.11.1 - creationTimestamp: null - name: alertmanagers.monitoring.coreos.com -spec: - group: monitoring.coreos.com - names: - categories: - - prometheus-operator - kind: Alertmanager - listKind: AlertmanagerList - plural: alertmanagers - shortNames: - - am - singular: alertmanager - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The version of Alertmanager - jsonPath: .spec.version - name: Version - type: string - - description: The number of desired replicas - jsonPath: .spec.replicas - name: Replicas - type: integer - - description: The number of ready replicas - jsonPath: .status.availableReplicas - name: Ready - type: integer - - jsonPath: .status.conditions[?(@.type == 'Reconciled')].status - name: Reconciled - type: string - - jsonPath: .status.conditions[?(@.type == 'Available')].status - name: Available - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Whether the resource reconciliation is paused or not - jsonPath: .status.paused - name: Paused - priority: 1 - type: boolean - name: v1 - schema: - openAPIV3Schema: - description: Alertmanager describes an Alertmanager cluster. - 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: 'Specification of the desired behavior of the Alertmanager - cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' - properties: - additionalPeers: - description: AdditionalPeers allows injecting a set of additional - Alertmanagers to peer with to form a highly available cluster. - items: - type: string - type: array - affinity: - description: If specified, the pod's scheduling constraints. - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the - pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods to - nodes that satisfy the affinity expressions specified by - this field, but it may choose a node that violates one or - more of the expressions. The node that is most preferred - is the one with the greatest sum of weights, i.e. for each - node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements of - this field and adding "weight" to the sum if the node matches - the corresponding matchExpressions; the node(s) with the - highest sum are the most preferred. - items: - description: An empty preferred scheduling term matches - all objects with implicit weight 0 (i.e. it's a no-op). - A null preferred scheduling term matches no objects (i.e. - is also a no-op). - properties: - preference: - description: A node selector term, associated with the - corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding - nodeSelectorTerm, in the range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by this - field are not met at scheduling time, the pod will not be - scheduled onto the node. If the affinity requirements specified - by this field cease to be met at some point during pod execution - (e.g. due to an update), the system may or may not try to - eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. - The terms are ORed. - items: - description: A null or empty node selector term matches - no objects. The requirements of them are ANDed. The - TopologySelectorTerm type implements a subset of the - NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - x-kubernetes-map-type: atomic - type: array - required: - - nodeSelectorTerms - type: object - x-kubernetes-map-type: atomic - type: object - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate - this pod in the same node, zone, etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods to - nodes that satisfy the affinity expressions specified by - this field, but it may choose a node that violates one or - more of the expressions. The node that is most preferred - is the one with the greatest sum of weights, i.e. for each - node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements of - this field and adding "weight" to the sum if the node has - pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching the corresponding - podAffinityTerm, in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by this - field are not met at scheduling time, the pod will not be - scheduled onto the node. If the affinity requirements specified - by this field cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may or may - not try to eventually evict the pod from its node. When - there are multiple elements, the lists of nodes corresponding - to each podAffinityTerm are intersected, i.e. all terms - must be satisfied. - items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not co-located - (anti-affinity) with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which a pod of the set of - pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied to the - union of the namespaces selected by this field and - the ones listed in the namespaces field. null selector - and null or empty namespaces list means "this pod's - namespace". An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static list of namespace - names that the term applies to. The term is applied - to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. null or - empty namespaces list and null namespaceSelector means - "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where - co-located is defined as running on a node whose value - of the label with key topologyKey matches that of - any node on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. - avoid putting this pod in the same node, zone, etc. as some - other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods to - nodes that satisfy the anti-affinity expressions specified - by this field, but it may choose a node that violates one - or more of the expressions. The node that is most preferred - is the one with the greatest sum of weights, i.e. for each - node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, - etc.), compute a sum by iterating through the elements of - this field and adding "weight" to the sum if the node has - pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching the corresponding - podAffinityTerm, in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the anti-affinity requirements - specified by this field cease to be met at some point during - pod execution (e.g. due to a pod label update), the system - may or may not try to eventually evict the pod from its - node. When there are multiple elements, the lists of nodes - corresponding to each podAffinityTerm are intersected, i.e. - all terms must be satisfied. - items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not co-located - (anti-affinity) with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which a pod of the set of - pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied to the - union of the namespaces selected by this field and - the ones listed in the namespaces field. null selector - and null or empty namespaces list means "this pod's - namespace". An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static list of namespace - names that the term applies to. The term is applied - to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. null or - empty namespaces list and null namespaceSelector means - "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where - co-located is defined as running on a node whose value - of the label with key topologyKey matches that of - any node on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - alertmanagerConfigMatcherStrategy: - description: The AlertmanagerConfigMatcherStrategy defines how AlertmanagerConfig - objects match the alerts. In the future more options may be added. - properties: - type: - default: OnNamespace - description: If set to `OnNamespace`, the operator injects a label - matcher matching the namespace of the AlertmanagerConfig object - for all its routes and inhibition rules. `None` will not add - any additional matchers other than the ones specified in the - AlertmanagerConfig. Default is `OnNamespace`. - enum: - - OnNamespace - - None - type: string - type: object - alertmanagerConfigNamespaceSelector: - description: Namespaces to be selected for AlertmanagerConfig discovery. - If nil, only check own namespace. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the key - and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship to - a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - alertmanagerConfigSelector: - description: AlertmanagerConfigs to be selected for to merge and configure - Alertmanager with. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the key - and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship to - a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - alertmanagerConfiguration: - description: 'EXPERIMENTAL: alertmanagerConfiguration specifies the - configuration of Alertmanager. If defined, it takes precedence over - the `configSecret` field. This field may change in future releases.' - properties: - global: - description: Defines the global parameters of the Alertmanager - configuration. - properties: - httpConfig: - description: HTTP client configuration. - properties: - authorization: - description: Authorization header configuration for the - client. This is mutually exclusive with BasicAuth and - is only available starting from Alertmanager v0.22+. - properties: - credentials: - description: The secret's key that contains the credentials - of the request - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults - to Bearer, Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth for the client. This is mutually - exclusive with Authorization. If both are defined, BasicAuth - takes precedence. - properties: - password: - description: The secret in the service monitor namespace - that contains the password for authentication. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor namespace - that contains the username for authentication. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: The secret's key that contains the bearer - token to be used by the client for authentication. The - secret needs to be in the same namespace as the Alertmanager - object and accessible by the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - followRedirects: - description: FollowRedirects specifies whether the client - should follow HTTP 3xx redirects. - type: boolean - oauth2: - description: OAuth2 client credentials used to fetch a - token for the targets. - properties: - clientId: - description: The secret or configmap containing the - OAuth2 client id - properties: - configMap: - description: ConfigMap containing data to use - for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for - the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 client - secret - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token URL - type: object - scopes: - description: OAuth2 scopes used for the token request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - proxyURL: - description: Optional proxy URL. - type: string - tlsConfig: - description: TLS configuration for the client. - properties: - ca: - description: Certificate authority used when verifying - server certificates. - properties: - configMap: - description: ConfigMap containing data to use - for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for - the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when doing - client-authentication. - properties: - configMap: - description: ConfigMap containing data to use - for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for - the targets. - properties: - key: - description: The key of the secret to select - from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key file - for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the targets. - type: string - type: object - type: object - opsGenieApiKey: - description: The default OpsGenie API Key. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - opsGenieApiUrl: - description: The default OpsGenie API URL. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - resolveTimeout: - description: ResolveTimeout is the default value used by alertmanager - if the alert does not include EndsAt, after this time passes - it can declare the alert as resolved if it has not been - updated. This has no impact on alerts from Prometheus, as - they always include EndsAt. - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - slackApiUrl: - description: The default Slack API URL. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - name: - description: The name of the AlertmanagerConfig resource which - is used to generate the Alertmanager configuration. It must - be defined in the same namespace as the Alertmanager object. - The operator will not enforce a `namespace` label for routes - and inhibition rules. - minLength: 1 - type: string - templates: - description: Custom notification templates. - items: - description: SecretOrConfigMap allows to specify data as a Secret - or ConfigMap. Fields are mutually exclusive. - properties: - configMap: - description: ConfigMap containing data to use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - type: array - type: object - baseImage: - description: 'Base image that is used to deploy pods, without tag. - Deprecated: use ''image'' instead' - type: string - clusterAdvertiseAddress: - description: 'ClusterAdvertiseAddress is the explicit address to advertise - in cluster. Needs to be provided for non RFC1918 [1] (public) addresses. - [1] RFC1918: https://tools.ietf.org/html/rfc1918' - type: string - clusterGossipInterval: - description: Interval between gossip attempts. - pattern: ^(0|(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - clusterPeerTimeout: - description: Timeout for cluster peering. - pattern: ^(0|(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - clusterPushpullInterval: - description: Interval between pushpull attempts. - pattern: ^(0|(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - configMaps: - description: ConfigMaps is a list of ConfigMaps in the same namespace - as the Alertmanager object, which shall be mounted into the Alertmanager - Pods. Each ConfigMap is added to the StatefulSet definition as a - volume named `configmap-`. The ConfigMaps are mounted - into `/etc/alertmanager/configmaps/` in the 'alertmanager' - container. - items: - type: string - type: array - configSecret: - description: "ConfigSecret is the name of a Kubernetes Secret in the - same namespace as the Alertmanager object, which contains the configuration - for this Alertmanager instance. If empty, it defaults to `alertmanager-`. - \n The Alertmanager configuration should be available under the - `alertmanager.yaml` key. Additional keys from the original secret - are copied to the generated secret and mounted into the `/etc/alertmanager/config` - directory in the `alertmanager` container. \n If either the secret - or the `alertmanager.yaml` key is missing, the operator provisions - a minimal Alertmanager configuration with one empty receiver (effectively - dropping alert notifications)." - type: string - containers: - description: 'Containers allows injecting additional containers. This - is meant to allow adding an authentication proxy to an Alertmanager - pod. Containers described here modify an operator generated container - if they share the same name and modifications are done via a strategic - merge patch. The current container names are: `alertmanager` and - `config-reloader`. Overriding containers is entirely outside the - scope of what the maintainers will support and by doing so, you - accept that this behaviour may break at any time without notice.' - items: - description: A single application container that you want to run - within a pod. - properties: - args: - description: 'Arguments to the entrypoint. The container image''s - CMD is used if this is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. If a variable - cannot be resolved, the reference in the input string will - be unchanged. Double $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will - produce the string literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed within a shell. - The container image''s ENTRYPOINT is used if this is not provided. - Variable references $(VAR_NAME) are expanded using the container''s - environment. If a variable cannot be resolved, the reference - in the input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) syntax: - i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless of whether - the variable exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables to set in the container. - Cannot be updated. - items: - description: EnvVar represents an environment variable present - in a Container. - properties: - name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in - the container and any service environment variables. - If a variable cannot be resolved, the reference in the - input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) - syntax: i.e. "$$(VAR_NAME)" will produce the string - literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists - or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. - Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: 'Selects a field of the pod: supports - metadata.name, metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the - specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, requests.cpu, - requests.memory and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the - exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's - namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment variables - in the container. The keys defined within a source must be - a C_IDENTIFIER. All invalid keys will be reported as an event - when the container is starting. When a key exists in multiple - sources, the value associated with the last source will take - precedence. Values defined by an Env with a duplicate key - will take precedence. Cannot be updated. - items: - description: EnvFromSource represents the source of a set - of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap must be - defined - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - type: object - type: array - image: - description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level config management - to default or override container images in workload controllers - like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, Never, IfNotPresent. - Defaults to Always if :latest tag is specified, or IfNotPresent - otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management system should take - in response to container lifecycle events. Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately after a container - is created. If the handler fails, the container is terminated - and restarted according to its restart policy. Other management - of the container blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for - the command is root ('/') in the container's - filesystem. The command is simply exec'd, it is - not run inside a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you need - to explicitly call out to that shell. Exit status - of 0 is treated as live/healthy and non-zero is - unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to - the pod IP. You probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the request. - HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the - host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward compatibility. - There are no validation of this field and lifecycle - hooks will fail in runtime when tcp handler is specified. - properties: - host: - description: 'Optional: Host name to connect to, - defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately before a container - is terminated due to an API request or management event - such as liveness/startup probe failure, preemption, resource - contention, etc. The handler is not called if the container - crashes or exits. The Pod''s termination grace period - countdown begins before the PreStop hook is executed. - Regardless of the outcome of the handler, the container - will eventually terminate within the Pod''s termination - grace period (unless delayed by finalizers). Other management - of the container blocks until the hook completes or until - the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for - the command is root ('/') in the container's - filesystem. The command is simply exec'd, it is - not run inside a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you need - to explicitly call out to that shell. Exit status - of 0 is treated as live/healthy and non-zero is - unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to - the pod IP. You probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the request. - HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the - host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward compatibility. - There are no validation of this field and lifecycle - hooks will fail in runtime when tcp handler is specified. - properties: - host: - description: 'Optional: Host name to connect to, - defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container liveness. Container - will be restarted if the probe fails. Cannot be updated. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified as a DNS_LABEL. - Each container in a pod must have a unique name (DNS_LABEL). - Cannot be updated. - type: string - ports: - description: List of ports to expose from the container. Not - specifying a port here DOES NOT prevent that port from being - exposed. Any port which is listening on the default "0.0.0.0" - address inside a container will be accessible from the network. - Modifying this array with strategic merge patch may corrupt - the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. - Cannot be updated. - items: - description: ContainerPort represents a network port in a - single container. - properties: - containerPort: - description: Number of port to expose on the pod's IP - address. This must be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external port to. - type: string - hostPort: - description: Number of port to expose on the host. If - specified, this must be a valid port number, 0 < x < - 65536. If HostNetwork is specified, this must match - ContainerPort. Most containers do not need this. - format: int32 - type: integer - name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port in a pod - must have a unique name. Name for the port that can - be referred to by services. - type: string - protocol: - default: TCP - description: Protocol for port. Must be UDP, TCP, or SCTP. - Defaults to "TCP". - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container service readiness. - Container will be removed from service endpoints if the probe - fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resizePolicy: - description: Resources resize policy for the container. - items: - description: ContainerResizePolicy represents resource resize - policy for the container. - properties: - resourceName: - description: 'Name of the resource to which this resource - resize policy applies. Supported values: cpu, memory.' - type: string - restartPolicy: - description: Restart policy to apply when specified resource - is resized. If not specified, it defaults to NotRequired. - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - description: 'Compute Resources required by this container. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: 'SecurityContext defines the security options the - container should be run with. If set, the fields of SecurityContext - override the equivalent fields of PodSecurityContext. More - info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether - a process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by - the container runtime. Note that this field cannot be - set when spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes - in privileged containers are essentially equivalent to - root on the host. Defaults to false. Note that this field - cannot be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to - use for the containers. The default is DefaultProcMount - which uses the container runtime defaults for readonly - paths and masked paths. This requires the ProcMountType - feature flag to be enabled. Note that this field cannot - be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root - filesystem. Default is false. Note that this field cannot - be set when spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a - non-root user. If true, the Kubelet will validate the - image at runtime to ensure that it does not run as UID - 0 (root) and fail to start the container if it does. If - unset or false, no such validation will be performed. - May also be set in PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a - random SELinux context for each container. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. - Note that this field cannot be set when spec.os.name is - windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile - must be preconfigured on the node to work. Must be - a descending path, relative to the kubelet's configured - seccomp profile location. Must only be set if type - is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - - a profile defined in a file on the node should be - used. RuntimeDefault - the container runtime default - profile should be used. Unconfined - no profile should - be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the - GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components - that enable the WindowsHostProcessContainers feature - flag. Setting this field without the feature flag - will result in errors when validating the Pod. All - of a Pod's containers must have the same effective - HostProcess value (it is not allowed to have a mix - of HostProcess containers and non-HostProcess containers). In - addition, if HostProcess is true then HostNetwork - must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that the Pod has successfully - initialized. If specified, no other probes are executed until - this completes successfully. If this probe fails, the Pod - will be restarted, just as if the livenessProbe failed. This - can be used to provide different probe parameters at the beginning - of a Pod''s lifecycle, when it might take a long time to load - data or warm a cache, than during steady-state operation. - This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should allocate a buffer - for stdin in the container runtime. If this is not set, reads - from stdin in the container will always result in EOF. Default - is false. - type: boolean - stdinOnce: - description: Whether the container runtime should close the - stdin channel after it has been opened by a single attach. - When stdin is true the stdin stream will remain open across - multiple attach sessions. If stdinOnce is set to true, stdin - is opened on container start, is empty until the first client - attaches to stdin, and then remains open and accepts data - until the client disconnects, at which time stdin is closed - and remains closed until the container is restarted. If this - flag is false, a container processes that reads from stdin - will never receive an EOF. Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the file to which the - container''s termination message will be written is mounted - into the container''s filesystem. Message written is intended - to be brief final status, such as an assertion failure message. - Will be truncated by the node if greater than 4096 bytes. - The total message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination message should be - populated. File will use the contents of terminationMessagePath - to populate the container status message on both success and - failure. FallbackToLogsOnError will use the last chunk of - container log output if the termination message file is empty - and the container exited with an error. The log output is - limited to 2048 bytes or 80 lines, whichever is smaller. Defaults - to File. Cannot be updated. - type: string - tty: - description: Whether this container should allocate a TTY for - itself, also requires 'stdin' to be true. Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of block devices to be - used by the container. - items: - description: volumeDevice describes a mapping of a raw block - device within a container. - properties: - devicePath: - description: devicePath is the path inside of the container - that the device will be mapped to. - type: string - name: - description: name must match the name of a persistentVolumeClaim - in the pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the container's filesystem. - Cannot be updated. - items: - description: VolumeMount describes a mounting of a Volume - within a container. - properties: - mountPath: - description: Path within the container at which the volume - should be mounted. Must not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines how mounts are - propagated from the host to container and the other - way around. When not set, MountPropagationNone is used. - This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write otherwise - (false or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume from which the container's - volume should be mounted. Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves similarly - to SubPath but environment variable references $(VAR_NAME) - are expanded using the container's environment. Defaults - to "" (volume's root). SubPathExpr and SubPath are mutually - exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. If not specified, - the container runtime's default will be used, which might - be configured in the container image. Cannot be updated. - type: string - required: - - name - type: object - type: array - externalUrl: - description: The external URL the Alertmanager instances will be available - under. This is necessary to generate correct URLs. This is necessary - if Alertmanager is not served from root of a DNS name. - type: string - forceEnableClusterMode: - description: ForceEnableClusterMode ensures Alertmanager does not - deactivate the cluster mode when running with a single replica. - Use case is e.g. spanning an Alertmanager cluster across Kubernetes - clusters with a single replica in each. - type: boolean - hostAliases: - description: Pods' hostAliases configuration - items: - description: HostAlias holds the mapping between IP and hostnames - that will be injected as an entry in the pod's hosts file. - properties: - hostnames: - description: Hostnames for the above IP address. - items: - type: string - type: array - ip: - description: IP address of the host file entry. - type: string - required: - - hostnames - - ip - type: object - type: array - x-kubernetes-list-map-keys: - - ip - x-kubernetes-list-type: map - image: - description: Image if specified has precedence over baseImage, tag - and sha combinations. Specifying the version is still necessary - to ensure the Prometheus Operator knows what version of Alertmanager - is being configured. - type: string - imagePullPolicy: - description: Image pull policy for the 'alertmanager', 'init-config-reloader' - and 'config-reloader' containers. See https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy - for more details. - enum: - - "" - - Always - - Never - - IfNotPresent - type: string - imagePullSecrets: - description: An optional list of references to secrets in the same - namespace to use for pulling prometheus and alertmanager images - from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod - items: - description: LocalObjectReference contains enough information to - let you locate the referenced object inside the same namespace. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - type: array - initContainers: - description: 'InitContainers allows adding initContainers to the pod - definition. Those can be used to e.g. fetch secrets for injection - into the Alertmanager configuration from external sources. Any errors - during the execution of an initContainer will lead to a restart - of the Pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ - InitContainers described here modify an operator generated init - containers if they share the same name and modifications are done - via a strategic merge patch. The current init container name is: - `init-config-reloader`. Overriding init containers is entirely outside - the scope of what the maintainers will support and by doing so, - you accept that this behaviour may break at any time without notice.' - items: - description: A single application container that you want to run - within a pod. - properties: - args: - description: 'Arguments to the entrypoint. The container image''s - CMD is used if this is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. If a variable - cannot be resolved, the reference in the input string will - be unchanged. Double $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will - produce the string literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed within a shell. - The container image''s ENTRYPOINT is used if this is not provided. - Variable references $(VAR_NAME) are expanded using the container''s - environment. If a variable cannot be resolved, the reference - in the input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) syntax: - i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless of whether - the variable exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables to set in the container. - Cannot be updated. - items: - description: EnvVar represents an environment variable present - in a Container. - properties: - name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in - the container and any service environment variables. - If a variable cannot be resolved, the reference in the - input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) - syntax: i.e. "$$(VAR_NAME)" will produce the string - literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists - or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. - Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: 'Selects a field of the pod: supports - metadata.name, metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the - specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, requests.cpu, - requests.memory and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the - exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's - namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment variables - in the container. The keys defined within a source must be - a C_IDENTIFIER. All invalid keys will be reported as an event - when the container is starting. When a key exists in multiple - sources, the value associated with the last source will take - precedence. Values defined by an Env with a duplicate key - will take precedence. Cannot be updated. - items: - description: EnvFromSource represents the source of a set - of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap must be - defined - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - type: object - type: array - image: - description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level config management - to default or override container images in workload controllers - like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, Never, IfNotPresent. - Defaults to Always if :latest tag is specified, or IfNotPresent - otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management system should take - in response to container lifecycle events. Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately after a container - is created. If the handler fails, the container is terminated - and restarted according to its restart policy. Other management - of the container blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for - the command is root ('/') in the container's - filesystem. The command is simply exec'd, it is - not run inside a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you need - to explicitly call out to that shell. Exit status - of 0 is treated as live/healthy and non-zero is - unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to - the pod IP. You probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the request. - HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the - host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward compatibility. - There are no validation of this field and lifecycle - hooks will fail in runtime when tcp handler is specified. - properties: - host: - description: 'Optional: Host name to connect to, - defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately before a container - is terminated due to an API request or management event - such as liveness/startup probe failure, preemption, resource - contention, etc. The handler is not called if the container - crashes or exits. The Pod''s termination grace period - countdown begins before the PreStop hook is executed. - Regardless of the outcome of the handler, the container - will eventually terminate within the Pod''s termination - grace period (unless delayed by finalizers). Other management - of the container blocks until the hook completes or until - the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for - the command is root ('/') in the container's - filesystem. The command is simply exec'd, it is - not run inside a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you need - to explicitly call out to that shell. Exit status - of 0 is treated as live/healthy and non-zero is - unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to - the pod IP. You probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the request. - HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the - host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward compatibility. - There are no validation of this field and lifecycle - hooks will fail in runtime when tcp handler is specified. - properties: - host: - description: 'Optional: Host name to connect to, - defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container liveness. Container - will be restarted if the probe fails. Cannot be updated. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified as a DNS_LABEL. - Each container in a pod must have a unique name (DNS_LABEL). - Cannot be updated. - type: string - ports: - description: List of ports to expose from the container. Not - specifying a port here DOES NOT prevent that port from being - exposed. Any port which is listening on the default "0.0.0.0" - address inside a container will be accessible from the network. - Modifying this array with strategic merge patch may corrupt - the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. - Cannot be updated. - items: - description: ContainerPort represents a network port in a - single container. - properties: - containerPort: - description: Number of port to expose on the pod's IP - address. This must be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external port to. - type: string - hostPort: - description: Number of port to expose on the host. If - specified, this must be a valid port number, 0 < x < - 65536. If HostNetwork is specified, this must match - ContainerPort. Most containers do not need this. - format: int32 - type: integer - name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port in a pod - must have a unique name. Name for the port that can - be referred to by services. - type: string - protocol: - default: TCP - description: Protocol for port. Must be UDP, TCP, or SCTP. - Defaults to "TCP". - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container service readiness. - Container will be removed from service endpoints if the probe - fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resizePolicy: - description: Resources resize policy for the container. - items: - description: ContainerResizePolicy represents resource resize - policy for the container. - properties: - resourceName: - description: 'Name of the resource to which this resource - resize policy applies. Supported values: cpu, memory.' - type: string - restartPolicy: - description: Restart policy to apply when specified resource - is resized. If not specified, it defaults to NotRequired. - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - description: 'Compute Resources required by this container. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: 'SecurityContext defines the security options the - container should be run with. If set, the fields of SecurityContext - override the equivalent fields of PodSecurityContext. More - info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether - a process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by - the container runtime. Note that this field cannot be - set when spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes - in privileged containers are essentially equivalent to - root on the host. Defaults to false. Note that this field - cannot be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to - use for the containers. The default is DefaultProcMount - which uses the container runtime defaults for readonly - paths and masked paths. This requires the ProcMountType - feature flag to be enabled. Note that this field cannot - be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root - filesystem. Default is false. Note that this field cannot - be set when spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a - non-root user. If true, the Kubelet will validate the - image at runtime to ensure that it does not run as UID - 0 (root) and fail to start the container if it does. If - unset or false, no such validation will be performed. - May also be set in PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a - random SELinux context for each container. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. - Note that this field cannot be set when spec.os.name is - windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile - must be preconfigured on the node to work. Must be - a descending path, relative to the kubelet's configured - seccomp profile location. Must only be set if type - is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - - a profile defined in a file on the node should be - used. RuntimeDefault - the container runtime default - profile should be used. Unconfined - no profile should - be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the - GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components - that enable the WindowsHostProcessContainers feature - flag. Setting this field without the feature flag - will result in errors when validating the Pod. All - of a Pod's containers must have the same effective - HostProcess value (it is not allowed to have a mix - of HostProcess containers and non-HostProcess containers). In - addition, if HostProcess is true then HostNetwork - must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that the Pod has successfully - initialized. If specified, no other probes are executed until - this completes successfully. If this probe fails, the Pod - will be restarted, just as if the livenessProbe failed. This - can be used to provide different probe parameters at the beginning - of a Pod''s lifecycle, when it might take a long time to load - data or warm a cache, than during steady-state operation. - This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should allocate a buffer - for stdin in the container runtime. If this is not set, reads - from stdin in the container will always result in EOF. Default - is false. - type: boolean - stdinOnce: - description: Whether the container runtime should close the - stdin channel after it has been opened by a single attach. - When stdin is true the stdin stream will remain open across - multiple attach sessions. If stdinOnce is set to true, stdin - is opened on container start, is empty until the first client - attaches to stdin, and then remains open and accepts data - until the client disconnects, at which time stdin is closed - and remains closed until the container is restarted. If this - flag is false, a container processes that reads from stdin - will never receive an EOF. Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the file to which the - container''s termination message will be written is mounted - into the container''s filesystem. Message written is intended - to be brief final status, such as an assertion failure message. - Will be truncated by the node if greater than 4096 bytes. - The total message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination message should be - populated. File will use the contents of terminationMessagePath - to populate the container status message on both success and - failure. FallbackToLogsOnError will use the last chunk of - container log output if the termination message file is empty - and the container exited with an error. The log output is - limited to 2048 bytes or 80 lines, whichever is smaller. Defaults - to File. Cannot be updated. - type: string - tty: - description: Whether this container should allocate a TTY for - itself, also requires 'stdin' to be true. Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of block devices to be - used by the container. - items: - description: volumeDevice describes a mapping of a raw block - device within a container. - properties: - devicePath: - description: devicePath is the path inside of the container - that the device will be mapped to. - type: string - name: - description: name must match the name of a persistentVolumeClaim - in the pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the container's filesystem. - Cannot be updated. - items: - description: VolumeMount describes a mounting of a Volume - within a container. - properties: - mountPath: - description: Path within the container at which the volume - should be mounted. Must not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines how mounts are - propagated from the host to container and the other - way around. When not set, MountPropagationNone is used. - This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write otherwise - (false or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume from which the container's - volume should be mounted. Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves similarly - to SubPath but environment variable references $(VAR_NAME) - are expanded using the container's environment. Defaults - to "" (volume's root). SubPathExpr and SubPath are mutually - exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. If not specified, - the container runtime's default will be used, which might - be configured in the container image. Cannot be updated. - type: string - required: - - name - type: object - type: array - listenLocal: - description: ListenLocal makes the Alertmanager server listen on loopback, - so that it does not bind against the Pod IP. Note this is only for - the Alertmanager UI, not the gossip communication. - type: boolean - logFormat: - description: Log format for Alertmanager to be configured with. - enum: - - "" - - logfmt - - json - type: string - logLevel: - description: Log level for Alertmanager to be configured with. - enum: - - "" - - debug - - info - - warn - - error - type: string - minReadySeconds: - description: Minimum number of seconds for which a newly created pod - should be ready without any of its container crashing for it to - be considered available. Defaults to 0 (pod will be considered available - as soon as it is ready) This is an alpha field from kubernetes 1.22 - until 1.24 which requires enabling the StatefulSetMinReadySeconds - feature gate. - format: int32 - type: integer - nodeSelector: - additionalProperties: - type: string - description: Define which Nodes the Pods are scheduled on. - type: object - paused: - description: If set to true all actions on the underlying managed - objects are not goint to be performed, except for delete actions. - type: boolean - podMetadata: - description: PodMetadata configures Labels and Annotations which are - propagated to the alertmanager pods. - properties: - annotations: - additionalProperties: - type: string - description: 'Annotations is an unstructured key value map stored - with a resource that may be set by external tools to store and - retrieve arbitrary metadata. They are not queryable and should - be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations' - type: object - labels: - additionalProperties: - type: string - description: 'Map of string keys and values that can be used to - organize and categorize (scope and select) objects. May match - selectors of replication controllers and services. More info: - http://kubernetes.io/docs/user-guide/labels' - type: object - name: - description: 'Name must be unique within a namespace. Is required - when creating resources, although some resources may allow a - client to request the generation of an appropriate name automatically. - Name is primarily intended for creation idempotence and configuration - definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names' - type: string - type: object - portName: - default: web - description: Port name used for the pods and governing service. Defaults - to `web`. - type: string - priorityClassName: - description: Priority class assigned to the Pods - type: string - replicas: - description: Size is the expected size of the alertmanager cluster. - The controller will eventually make the size of the running cluster - equal to the expected size. - format: int32 - type: integer - resources: - description: Define resources requests and limits for single Pods. - properties: - claims: - description: "Claims lists the names of resources, defined in - spec.resourceClaims, that are used by this container. \n This - is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be set - for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in pod.spec.resourceClaims - of the Pod where this field is used. It makes that resource - available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources - allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - retention: - default: 120h - description: Time duration Alertmanager shall retain data for. Default - is '120h', and must match the regular expression `[0-9]+(ms|s|m|h)` - (milliseconds seconds minutes hours). - pattern: ^(0|(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - routePrefix: - description: The route prefix Alertmanager registers HTTP handlers - for. This is useful, if using ExternalURL and a proxy is rewriting - HTTP routes of a request, and the actual ExternalURL is still true, - but the server serves requests under a different route prefix. For - example for use with `kubectl proxy`. - type: string - secrets: - description: Secrets is a list of Secrets in the same namespace as - the Alertmanager object, which shall be mounted into the Alertmanager - Pods. Each Secret is added to the StatefulSet definition as a volume - named `secret-`. The Secrets are mounted into `/etc/alertmanager/secrets/` - in the 'alertmanager' container. - items: - type: string - type: array - securityContext: - description: SecurityContext holds pod-level security attributes and - common container settings. This defaults to the default PodSecurityContext. - properties: - fsGroup: - description: "A special supplemental group that applies to all - containers in a pod. Some volume types allow the Kubelet to - change the ownership of that volume to be owned by the pod: - \n 1. The owning GID will be the FSGroup 2. The setgid bit is - set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- \n If unset, - the Kubelet will not modify the ownership and permissions of - any volume. Note that this field cannot be set when spec.os.name - is windows." - format: int64 - type: integer - fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of changing - ownership and permission of the volume before being exposed - inside Pod. This field will only apply to volume types which - support fsGroup based ownership(and permissions). It will have - no effect on ephemeral volume types such as: secret, configmaps - and emptydir. Valid values are "OnRootMismatch" and "Always". - If not specified, "Always" is used. Note that this field cannot - be set when spec.os.name is windows.' - type: string - runAsGroup: - description: The GID to run the entrypoint of the container process. - Uses runtime default if unset. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail to start - the container if it does. If unset or false, no such validation - will be performed. May also be set in SecurityContext. If set - in both SecurityContext and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this field cannot - be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies to - the container. - type: string - role: - description: Role is a SELinux role label that applies to - the container. - type: string - type: - description: Type is a SELinux type label that applies to - the container. - type: string - user: - description: User is a SELinux user label that applies to - the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by the containers in this - pod. Note that this field cannot be set when spec.os.name is - windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must be - preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a profile - defined in a file on the node should be used. RuntimeDefault - - the container runtime default profile should be used. - Unconfined - no profile should be applied." - type: string - required: - - type - type: object - supplementalGroups: - description: A list of groups applied to the first process run - in each container, in addition to the container's primary GID, - the fsGroup (if specified), and group memberships defined in - the container image for the uid of the container process. If - unspecified, no additional groups are added to any container. - Note that group memberships defined in the container image for - the uid of the container process are still effective, even if - they are not included in this list. Note that this field cannot - be set when spec.os.name is windows. - items: - format: int64 - type: integer - type: array - sysctls: - description: Sysctls hold a list of namespaced sysctls used for - the pod. Pods with unsupported sysctls (by the container runtime) - might fail to launch. Note that this field cannot be set when - spec.os.name is windows. - items: - description: Sysctl defines a kernel parameter to be set - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - description: The Windows specific settings applied to all containers. - If unspecified, the options within a container's SecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named by - the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the GMSA - credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is alpha-level - and will only be honored by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature flag - will result in errors when validating the Pod. All of a - Pod's containers must have the same effective HostProcess - value (it is not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in PodSecurityContext. - If set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - type: string - type: object - type: object - serviceAccountName: - description: ServiceAccountName is the name of the ServiceAccount - to use to run the Prometheus Pods. - type: string - sha: - description: 'SHA of Alertmanager container image to be deployed. - Defaults to the value of `version`. Similar to a tag, but the SHA - explicitly deploys an immutable container image. Version and Tag - are ignored if SHA is set. Deprecated: use ''image'' instead. The - image digest can be specified as part of the image URL.' - type: string - storage: - description: Storage is the definition of how storage will be used - by the Alertmanager instances. - properties: - disableMountSubPath: - description: 'Deprecated: subPath usage will be disabled by default - in a future release, this option will become unnecessary. DisableMountSubPath - allows to remove any subPath usage in volume mounts.' - type: boolean - emptyDir: - description: 'EmptyDirVolumeSource to be used by the StatefulSet. - If specified, used in place of any volumeClaimTemplate. More - info: https://kubernetes.io/docs/concepts/storage/volumes/#emptydir' - properties: - medium: - description: 'medium represents what type of storage medium - should back this directory. The default is "" which means - to use the node''s default medium. Must be an empty string - (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'sizeLimit is the total amount of local storage - required for this EmptyDir volume. The size limit is also - applicable for memory medium. The maximum usage on memory - medium EmptyDir would be the minimum value between the SizeLimit - specified here and the sum of memory limits of all containers - in a pod. The default is nil which means that the limit - is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: 'EphemeralVolumeSource to be used by the StatefulSet. - This is a beta field in k8s 1.21, for lower versions, starting - with k8s 1.19, it requires enabling the GenericEphemeralVolume - feature gate. More info: https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/#generic-ephemeral-volumes' - properties: - volumeClaimTemplate: - description: "Will be used to create a stand-alone PVC to - provision the volume. The pod in which this EphemeralVolumeSource - is embedded will be the owner of the PVC, i.e. the PVC will - be deleted together with the pod. The name of the PVC will - be `-` where `` is the - name from the `PodSpec.Volumes` array entry. Pod validation - will reject the pod if the concatenated name is not valid - for a PVC (for example, too long). \n An existing PVC with - that name that is not owned by the pod will *not* be used - for the pod to avoid using an unrelated volume by mistake. - Starting the pod is then blocked until the unrelated PVC - is removed. If such a pre-created PVC is meant to be used - by the pod, the PVC has to updated with an owner reference - to the pod once the pod exists. Normally this should not - be necessary, but it may be useful when manually reconstructing - a broken cluster. \n This field is read-only and no changes - will be made by Kubernetes to the PVC after it has been - created. \n Required, must not be nil." - properties: - metadata: - description: May contain labels and annotations that will - be copied into the PVC when creating it. No other fields - are allowed and will be rejected during validation. - type: object - spec: - description: The specification for the PersistentVolumeClaim. - The entire content is copied unchanged into the PVC - that gets created from this template. The same fields - as in a PersistentVolumeClaim are also valid here. - properties: - accessModes: - description: 'accessModes contains the desired access - modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'dataSource field can be used to specify - either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the - provisioner or an external controller can support - the specified data source, it will create a new - volume based on the contents of the specified data - source. When the AnyVolumeDataSource feature gate - is enabled, dataSource contents will be copied to - dataSourceRef, and dataSourceRef contents will be - copied to dataSource when dataSourceRef.namespace - is not specified. If the namespace is specified, - then dataSourceRef will not be copied to dataSource.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. - For any other third-party types, APIGroup is - required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - description: 'dataSourceRef specifies the object from - which to populate the volume with data, if a non-empty - volume is desired. This may be any object from a - non-empty API group (non core object) or a PersistentVolumeClaim - object. When this field is specified, volume binding - will only succeed if the type of the specified object - matches some installed volume populator or dynamic - provisioner. This field will replace the functionality - of the dataSource field and as such if both fields - are non-empty, they must have the same value. For - backwards compatibility, when namespace isn''t specified - in dataSourceRef, both fields (dataSource and dataSourceRef) - will be set to the same value automatically if one - of them is empty and the other is non-empty. When - namespace is specified in dataSourceRef, dataSource - isn''t set to the same value and must be empty. - There are three important differences between dataSource - and dataSourceRef: * While dataSource only allows - two specific types of objects, dataSourceRef allows - any non-core object, as well as PersistentVolumeClaim - objects. * While dataSource ignores disallowed values - (dropping them), dataSourceRef preserves all values, - and generates an error if a disallowed value is - specified. * While dataSource only allows local - objects, dataSourceRef allows objects in any namespaces. - (Beta) Using this field requires the AnyVolumeDataSource - feature gate to be enabled. (Alpha) Using the namespace - field of dataSourceRef requires the CrossNamespaceVolumeDataSource - feature gate to be enabled.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. - For any other third-party types, APIGroup is - required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - namespace: - description: Namespace is the namespace of resource - being referenced Note that when a namespace - is specified, a gateway.networking.k8s.io/ReferenceGrant - object is required in the referent namespace - to allow that namespace's owner to accept the - reference. See the ReferenceGrant documentation - for details. (Alpha) This field requires the - CrossNamespaceVolumeDataSource feature gate - to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify - resource requirements that are lower than previous - value but must still be higher than capacity recorded - in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used - by this container. \n This is an alpha field - and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It - can only be set for containers." - items: - description: ResourceClaim references one entry - in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of - one entry in pod.spec.resourceClaims of - the Pod where this field is used. It makes - that resource available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is - omitted for a container, it defaults to Limits - if that is explicitly specified, otherwise to - an implementation-defined value. Requests cannot - exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: selector is a label query over volumes - to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement is - a selector that contains values, a key, and - an operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If - the operator is Exists or DoesNotExist, - the values array must be empty. This array - is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: 'storageClassName is the name of the - StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines what type of volume - is required by the claim. Value of Filesystem is - implied when not included in claim spec. - type: string - volumeName: - description: volumeName is the binding reference to - the PersistentVolume backing this claim. - type: string - type: object - required: - - spec - type: object - type: object - volumeClaimTemplate: - description: A PVC spec to be used by the StatefulSet. The easiest - way to use a volume that cannot be automatically provisioned - (for whatever reason) is to use a label selector alongside manually - created PersistentVolumes. - 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: - description: EmbeddedMetadata contains metadata relevant to - an EmbeddedResource. - properties: - annotations: - additionalProperties: - type: string - description: 'Annotations is an unstructured key value - map stored with a resource that may be set by external - tools to store and retrieve arbitrary metadata. They - are not queryable and should be preserved when modifying - objects. More info: http://kubernetes.io/docs/user-guide/annotations' - type: object - labels: - additionalProperties: - type: string - description: 'Map of string keys and values that can be - used to organize and categorize (scope and select) objects. - May match selectors of replication controllers and services. - More info: http://kubernetes.io/docs/user-guide/labels' - type: object - name: - description: 'Name must be unique within a namespace. - Is required when creating resources, although some resources - may allow a client to request the generation of an appropriate - name automatically. Name is primarily intended for creation - idempotence and configuration definition. Cannot be - updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names' - type: string - type: object - spec: - description: 'Spec defines the desired characteristics of - a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - accessModes: - description: 'accessModes contains the desired access - modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'dataSource field can be used to specify - either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the provisioner - or an external controller can support the specified - data source, it will create a new volume based on the - contents of the specified data source. When the AnyVolumeDataSource - feature gate is enabled, dataSource contents will be - copied to dataSourceRef, and dataSourceRef contents - will be copied to dataSource when dataSourceRef.namespace - is not specified. If the namespace is specified, then - dataSourceRef will not be copied to dataSource.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - description: 'dataSourceRef specifies the object from - which to populate the volume with data, if a non-empty - volume is desired. This may be any object from a non-empty - API group (non core object) or a PersistentVolumeClaim - object. When this field is specified, volume binding - will only succeed if the type of the specified object - matches some installed volume populator or dynamic provisioner. - This field will replace the functionality of the dataSource - field and as such if both fields are non-empty, they - must have the same value. For backwards compatibility, - when namespace isn''t specified in dataSourceRef, both - fields (dataSource and dataSourceRef) will be set to - the same value automatically if one of them is empty - and the other is non-empty. When namespace is specified - in dataSourceRef, dataSource isn''t set to the same - value and must be empty. There are three important differences - between dataSource and dataSourceRef: * While dataSource - only allows two specific types of objects, dataSourceRef - allows any non-core object, as well as PersistentVolumeClaim - objects. * While dataSource ignores disallowed values - (dropping them), dataSourceRef preserves all values, - and generates an error if a disallowed value is specified. - * While dataSource only allows local objects, dataSourceRef - allows objects in any namespaces. (Beta) Using this - field requires the AnyVolumeDataSource feature gate - to be enabled. (Alpha) Using the namespace field of - dataSourceRef requires the CrossNamespaceVolumeDataSource - feature gate to be enabled.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - namespace: - description: Namespace is the namespace of resource - being referenced Note that when a namespace is specified, - a gateway.networking.k8s.io/ReferenceGrant object - is required in the referent namespace to allow that - namespace's owner to accept the reference. See the - ReferenceGrant documentation for details. (Alpha) - This field requires the CrossNamespaceVolumeDataSource - feature gate to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify resource - requirements that are lower than previous value but - must still be higher than capacity recorded in the status - field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used by - this container. \n This is an alpha field and requires - enabling the DynamicResourceAllocation feature gate. - \n This field is immutable. It can only be set for - containers." - items: - description: ResourceClaim references one entry - in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one - entry in pod.spec.resourceClaims of the Pod - where this field is used. It makes that resource - available inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is omitted - for a container, it defaults to Limits if that is - explicitly specified, otherwise to an implementation-defined - value. Requests cannot exceed Limits. More info: - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: selector is a label query over volumes to - consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values - array must be non-empty. If the operator is - Exists or DoesNotExist, the values array must - be empty. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: 'storageClassName is the name of the StorageClass - required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines what type of volume is - required by the claim. Value of Filesystem is implied - when not included in claim spec. - type: string - volumeName: - description: volumeName is the binding reference to the - PersistentVolume backing this claim. - type: string - type: object - status: - description: 'Status represents the current information/status - of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - accessModes: - description: 'accessModes contains the actual access modes - the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - allocatedResources: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: allocatedResources is the storage resource - within AllocatedResources tracks the capacity allocated - to a PVC. It may be larger than the actual capacity - when a volume expansion operation is requested. For - storage quota, the larger value from allocatedResources - and PVC.spec.resources is used. If allocatedResources - is not set, PVC.spec.resources alone is used for quota - calculation. If a volume expansion capacity request - is lowered, allocatedResources is only lowered if there - are no expansion operations in progress and if the actual - volume capacity is equal or lower than the requested - capacity. This is an alpha field and requires enabling - RecoverVolumeExpansionFailure feature. - type: object - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: capacity represents the actual resources - of the underlying volume. - type: object - conditions: - description: conditions is the current Condition of persistent - volume claim. If underlying persistent volume is being - resized then the Condition will be set to 'ResizeStarted'. - items: - description: PersistentVolumeClaimCondition contains - details about state of pvc - properties: - lastProbeTime: - description: lastProbeTime is the time we probed - the condition. - format: date-time - type: string - lastTransitionTime: - description: lastTransitionTime is the time the - condition transitioned from one status to another. - format: date-time - type: string - message: - description: message is the human-readable message - indicating details about last transition. - type: string - reason: - description: reason is a unique, this should be - a short, machine understandable string that gives - the reason for condition's last transition. If - it reports "ResizeStarted" that means the underlying - persistent volume is being resized. - type: string - status: - type: string - type: - description: PersistentVolumeClaimConditionType - is a valid value of PersistentVolumeClaimCondition.Type - type: string - required: - - status - - type - type: object - type: array - phase: - description: phase represents the current phase of PersistentVolumeClaim. - type: string - resizeStatus: - description: resizeStatus stores status of resize operation. - ResizeStatus is not set by default but when expansion - is complete resizeStatus is set to empty string by resize - controller or kubelet. This is an alpha field and requires - enabling RecoverVolumeExpansionFailure feature. - type: string - type: object - type: object - type: object - tag: - description: 'Tag of Alertmanager container image to be deployed. - Defaults to the value of `version`. Version is ignored if Tag is - set. Deprecated: use ''image'' instead. The image tag can be specified - as part of the image URL.' - type: string - tolerations: - description: If specified, the pod's tolerations. - items: - description: The pod this Toleration is attached to tolerates any - taint that matches the triple using the matching - operator . - properties: - effect: - description: Effect indicates the taint effect to match. Empty - means match all taint effects. When specified, allowed values - are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match all - values and all keys. - type: string - operator: - description: Operator represents a key's relationship to the - value. Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod - can tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of time - the toleration (which must be of effect NoExecute, otherwise - this field is ignored) tolerates the taint. By default, it - is not set, which means tolerate the taint forever (do not - evict). Zero and negative values will be treated as 0 (evict - immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. - type: string - type: object - type: array - topologySpreadConstraints: - description: If specified, the pod's topology spread constraints. - items: - description: TopologySpreadConstraint specifies how to spread matching - pods among the given topology. - properties: - labelSelector: - description: LabelSelector is used to find matching pods. Pods - that match this label selector are counted to determine the - number of pods in their corresponding topology domain. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists or - DoesNotExist, the values array must be empty. This - array is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: "MatchLabelKeys is a set of pod label keys to select - the pods over which spreading will be calculated. The keys - are used to lookup values from the incoming pod labels, those - key-value labels are ANDed with labelSelector to select the - group of existing pods over which spreading will be calculated - for the incoming pod. The same key is forbidden to exist in - both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot - be set when LabelSelector isn't set. Keys that don't exist - in the incoming pod labels will be ignored. A null or empty - list means only match against labelSelector. \n This is a - beta field and requires the MatchLabelKeysInPodTopologySpread - feature gate to be enabled (enabled by default)." - items: - type: string - type: array - x-kubernetes-list-type: atomic - maxSkew: - description: 'MaxSkew describes the degree to which pods may - be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, - it is the maximum permitted difference between the number - of matching pods in the target topology and the global minimum. - The global minimum is the minimum number of matching pods - in an eligible domain or zero if the number of eligible domains - is less than MinDomains. For example, in a 3-zone cluster, - MaxSkew is set to 1, and pods with the same labelSelector - spread as 2/2/1: In this case, the global minimum is 1. | - zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew - is 1, incoming pod can only be scheduled to zone3 to become - 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) - on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming - pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, - it is used to give higher precedence to topologies that satisfy - it. It''s a required field. Default value is 1 and 0 is not - allowed.' - format: int32 - type: integer - minDomains: - description: "MinDomains indicates a minimum number of eligible - domains. When the number of eligible domains with matching - topology keys is less than minDomains, Pod Topology Spread - treats \"global minimum\" as 0, and then the calculation of - Skew is performed. And when the number of eligible domains - with matching topology keys equals or greater than minDomains, - this value has no effect on scheduling. As a result, when - the number of eligible domains is less than minDomains, scheduler - won't schedule more than maxSkew Pods to those domains. If - value is nil, the constraint behaves as if MinDomains is equal - to 1. Valid values are integers greater than 0. When value - is not nil, WhenUnsatisfiable must be DoNotSchedule. \n For - example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains - is set to 5 and pods with the same labelSelector spread as - 2/2/2: | zone1 | zone2 | zone3 | | P P | P P | P P | - The number of domains is less than 5(MinDomains), so \"global - minimum\" is treated as 0. In this situation, new pod with - the same labelSelector cannot be scheduled, because computed - skew will be 3(3 - 0) if new Pod is scheduled to any of the - three zones, it will violate MaxSkew. \n This is a beta field - and requires the MinDomainsInPodTopologySpread feature gate - to be enabled (enabled by default)." - format: int32 - type: integer - nodeAffinityPolicy: - description: "NodeAffinityPolicy indicates how we will treat - Pod's nodeAffinity/nodeSelector when calculating pod topology - spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector - are included in the calculations. - Ignore: nodeAffinity/nodeSelector - are ignored. All nodes are included in the calculations. \n - If this value is nil, the behavior is equivalent to the Honor - policy. This is a beta-level feature default enabled by the - NodeInclusionPolicyInPodTopologySpread feature flag." - type: string - nodeTaintsPolicy: - description: "NodeTaintsPolicy indicates how we will treat node - taints when calculating pod topology spread skew. Options - are: - Honor: nodes without taints, along with tainted nodes - for which the incoming pod has a toleration, are included. - - Ignore: node taints are ignored. All nodes are included. - \n If this value is nil, the behavior is equivalent to the - Ignore policy. This is a beta-level feature default enabled - by the NodeInclusionPolicyInPodTopologySpread feature flag." - type: string - topologyKey: - description: TopologyKey is the key of node labels. Nodes that - have a label with this key and identical values are considered - to be in the same topology. We consider each - as a "bucket", and try to put balanced number of pods into - each bucket. We define a domain as a particular instance of - a topology. Also, we define an eligible domain as a domain - whose nodes meet the requirements of nodeAffinityPolicy and - nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname", - each Node is a domain of that topology. And, if TopologyKey - is "topology.kubernetes.io/zone", each zone is a domain of - that topology. It's a required field. - type: string - whenUnsatisfiable: - description: 'WhenUnsatisfiable indicates how to deal with a - pod if it doesn''t satisfy the spread constraint. - DoNotSchedule - (default) tells the scheduler not to schedule it. - ScheduleAnyway - tells the scheduler to schedule the pod in any location, but - giving higher precedence to topologies that would help reduce - the skew. A constraint is considered "Unsatisfiable" for an - incoming pod if and only if every possible node assignment - for that pod would violate "MaxSkew" on some topology. For - example, in a 3-zone cluster, MaxSkew is set to 1, and pods - with the same labelSelector spread as 3/1/1: | zone1 | zone2 - | zone3 | | P P P | P | P | If WhenUnsatisfiable is - set to DoNotSchedule, incoming pod can only be scheduled to - zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on - zone2(zone3) satisfies MaxSkew(1). In other words, the cluster - can still be imbalanced, but scheduler won''t make it *more* - imbalanced. It''s a required field.' - type: string - required: - - maxSkew - - topologyKey - - whenUnsatisfiable - type: object - type: array - version: - description: Version the cluster should be on. - type: string - volumeMounts: - description: VolumeMounts allows configuration of additional VolumeMounts - on the output StatefulSet definition. VolumeMounts specified will - be appended to other VolumeMounts in the alertmanager container, - that are generated as a result of StorageSpec objects. - items: - description: VolumeMount describes a mounting of a Volume within - a container. - properties: - mountPath: - description: Path within the container at which the volume should - be mounted. Must not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines how mounts are propagated - from the host to container and the other way around. When - not set, MountPropagationNone is used. This field is beta - in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write otherwise - (false or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume from which the container's - volume should be mounted. Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume from which the - container's volume should be mounted. Behaves similarly to - SubPath but environment variable references $(VAR_NAME) are - expanded using the container's environment. Defaults to "" - (volume's root). SubPathExpr and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - description: Volumes allows configuration of additional volumes on - the output StatefulSet definition. Volumes specified will be appended - to other volumes that are generated as a result of StorageSpec objects. - items: - description: Volume represents a named volume in a pod that may - be accessed by any container in the pod. - properties: - awsElasticBlockStore: - description: 'awsElasticBlockStore represents an AWS Disk resource - that is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - properties: - fsType: - description: 'fsType is the filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from - compromising the machine' - type: string - partition: - description: 'partition is the partition in the volume that - you want to mount. If omitted, the default is to mount - by volume name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property empty).' - format: int32 - type: integer - readOnly: - description: 'readOnly value true will force the readOnly - setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: boolean - volumeID: - description: 'volumeID is unique ID of the persistent disk - resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: string - required: - - volumeID - type: object - azureDisk: - description: azureDisk represents an Azure Data Disk mount on - the host and bind mount to the pod. - properties: - cachingMode: - description: 'cachingMode is the Host Caching mode: None, - Read Only, Read Write.' - type: string - diskName: - description: diskName is the Name of the data disk in the - blob storage - type: string - diskURI: - description: diskURI is the URI of data disk in the blob - storage - type: string - fsType: - description: fsType is Filesystem type to mount. Must be - a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. - type: string - kind: - description: 'kind expected values are Shared: multiple - blob disks per storage account Dedicated: single blob - disk per storage account Managed: azure managed data - disk (only in managed availability set). defaults to shared' - type: string - readOnly: - description: readOnly Defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: azureFile represents an Azure File Service mount - on the host and bind mount to the pod. - properties: - readOnly: - description: readOnly defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - secretName: - description: secretName is the name of secret that contains - Azure Storage Account Name and Key - type: string - shareName: - description: shareName is the azure share Name - type: string - required: - - secretName - - shareName - type: object - cephfs: - description: cephFS represents a Ceph FS mount on the host that - shares a pod's lifetime - properties: - monitors: - description: 'monitors is Required: Monitors is a collection - of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - items: - type: string - type: array - path: - description: 'path is Optional: Used as the mounted root, - rather than the full Ceph tree, default is /' - type: string - readOnly: - description: 'readOnly is Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: boolean - secretFile: - description: 'secretFile is Optional: SecretFile is the - path to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - secretRef: - description: 'secretRef is Optional: SecretRef is reference - to the authentication secret for User, default is empty. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - user: - description: 'user is optional: User is the rados user name, - default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - required: - - monitors - type: object - cinder: - description: 'cinder represents a cinder volume attached and - mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - properties: - fsType: - description: 'fsType is the filesystem type to mount. Must - be a filesystem type supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - readOnly: - description: 'readOnly defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: boolean - secretRef: - description: 'secretRef is optional: points to a secret - object containing parameters used to connect to OpenStack.' - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - volumeID: - description: 'volumeID used to identify the volume in cinder. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - required: - - volumeID - type: object - configMap: - description: configMap represents a configMap that should populate - this volume - properties: - defaultMode: - description: 'defaultMode is optional: mode bits used to - set permissions on created files by default. Must be an - octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. Defaults to - 0644. Directories within the path are not affected by - this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - items: - description: items if unspecified, each key-value pair in - the Data field of the referenced ConfigMap will be projected - into the volume as a file whose name is the key and content - is the value. If specified, the listed keys will be projected - into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in - the ConfigMap, the volume setup will error unless it is - marked optional. Paths must be relative and may not contain - the '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode bits used to - set permissions on this file. Must be an octal value - between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. If not - specified, the volume defaultMode will be used. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - path: - description: path is the relative path of the file - to map the key to. May not be an absolute path. - May not contain the path element '..'. May not start - with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: optional specify whether the ConfigMap or its - keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - description: csi (Container Storage Interface) represents ephemeral - storage that is handled by certain external CSI drivers (Beta - feature). - properties: - driver: - description: driver is the name of the CSI driver that handles - this volume. Consult with your admin for the correct name - as registered in the cluster. - type: string - fsType: - description: fsType to mount. Ex. "ext4", "xfs", "ntfs". - If not provided, the empty value is passed to the associated - CSI driver which will determine the default filesystem - to apply. - type: string - nodePublishSecretRef: - description: nodePublishSecretRef is a reference to the - secret object containing sensitive information to pass - to the CSI driver to complete the CSI NodePublishVolume - and NodeUnpublishVolume calls. This field is optional, - and may be empty if no secret is required. If the secret - object contains more than one secret, all secret references - are passed. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - readOnly: - description: readOnly specifies a read-only configuration - for the volume. Defaults to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: volumeAttributes stores driver-specific properties - that are passed to the CSI driver. Consult your driver's - documentation for supported values. - type: object - required: - - driver - type: object - downwardAPI: - description: downwardAPI represents downward API about the pod - that should populate this volume - properties: - defaultMode: - description: 'Optional: mode bits to use on created files - by default. Must be a Optional: mode bits used to set - permissions on created files by default. Must be an octal - value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. Defaults to - 0644. Directories within the path are not affected by - this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - items: - description: Items is a list of downward API volume file - items: - description: DownwardAPIVolumeFile represents information - to create the file containing the pod field - properties: - fieldRef: - description: 'Required: Selects a field of the pod: - only annotations, labels, name and namespace are - supported.' - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the - specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - description: 'Optional: mode bits used to set permissions - on this file, must be an octal value between 0000 - and 0777 or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON requires - decimal values for mode bits. If not specified, - the volume defaultMode will be used. This might - be in conflict with other options that affect the - file mode, like fsGroup, and the result can be other - mode bits set.' - format: int32 - type: integer - path: - description: 'Required: Path is the relative path - name of the file to be created. Must not be absolute - or contain the ''..'' path. Must be utf-8 encoded. - The first item of the relative path must not start - with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, requests.cpu and requests.memory) - are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the - exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - type: object - emptyDir: - description: 'emptyDir represents a temporary directory that - shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - properties: - medium: - description: 'medium represents what type of storage medium - should back this directory. The default is "" which means - to use the node''s default medium. Must be an empty string - (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'sizeLimit is the total amount of local storage - required for this EmptyDir volume. The size limit is also - applicable for memory medium. The maximum usage on memory - medium EmptyDir would be the minimum value between the - SizeLimit specified here and the sum of memory limits - of all containers in a pod. The default is nil which means - that the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: "ephemeral represents a volume that is handled - by a cluster storage driver. The volume's lifecycle is tied - to the pod that defines it - it will be created before the - pod starts, and deleted when the pod is removed. \n Use this - if: a) the volume is only needed while the pod runs, b) features - of normal volumes like restoring from snapshot or capacity - tracking are needed, c) the storage driver is specified through - a storage class, and d) the storage driver supports dynamic - volume provisioning through a PersistentVolumeClaim (see EphemeralVolumeSource - for more information on the connection between this volume - type and PersistentVolumeClaim). \n Use PersistentVolumeClaim - or one of the vendor-specific APIs for volumes that persist - for longer than the lifecycle of an individual pod. \n Use - CSI for light-weight local ephemeral volumes if the CSI driver - is meant to be used that way - see the documentation of the - driver for more information. \n A pod can use both types of - ephemeral volumes and persistent volumes at the same time." - properties: - volumeClaimTemplate: - description: "Will be used to create a stand-alone PVC to - provision the volume. The pod in which this EphemeralVolumeSource - is embedded will be the owner of the PVC, i.e. the PVC - will be deleted together with the pod. The name of the - PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. - Pod validation will reject the pod if the concatenated - name is not valid for a PVC (for example, too long). \n - An existing PVC with that name that is not owned by the - pod will *not* be used for the pod to avoid using an unrelated - volume by mistake. Starting the pod is then blocked until - the unrelated PVC is removed. If such a pre-created PVC - is meant to be used by the pod, the PVC has to updated - with an owner reference to the pod once the pod exists. - Normally this should not be necessary, but it may be useful - when manually reconstructing a broken cluster. \n This - field is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. \n Required, must - not be nil." - properties: - metadata: - description: May contain labels and annotations that - will be copied into the PVC when creating it. No other - fields are allowed and will be rejected during validation. - type: object - spec: - description: The specification for the PersistentVolumeClaim. - The entire content is copied unchanged into the PVC - that gets created from this template. The same fields - as in a PersistentVolumeClaim are also valid here. - properties: - accessModes: - description: 'accessModes contains the desired access - modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'dataSource field can be used to specify - either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the - provisioner or an external controller can support - the specified data source, it will create a new - volume based on the contents of the specified - data source. When the AnyVolumeDataSource feature - gate is enabled, dataSource contents will be copied - to dataSourceRef, and dataSourceRef contents will - be copied to dataSource when dataSourceRef.namespace - is not specified. If the namespace is specified, - then dataSourceRef will not be copied to dataSource.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API - group. For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - description: 'dataSourceRef specifies the object - from which to populate the volume with data, if - a non-empty volume is desired. This may be any - object from a non-empty API group (non core object) - or a PersistentVolumeClaim object. When this field - is specified, volume binding will only succeed - if the type of the specified object matches some - installed volume populator or dynamic provisioner. - This field will replace the functionality of the - dataSource field and as such if both fields are - non-empty, they must have the same value. For - backwards compatibility, when namespace isn''t - specified in dataSourceRef, both fields (dataSource - and dataSourceRef) will be set to the same value - automatically if one of them is empty and the - other is non-empty. When namespace is specified - in dataSourceRef, dataSource isn''t set to the - same value and must be empty. There are three - important differences between dataSource and dataSourceRef: - * While dataSource only allows two specific types - of objects, dataSourceRef allows any non-core - object, as well as PersistentVolumeClaim objects. - * While dataSource ignores disallowed values (dropping - them), dataSourceRef preserves all values, and - generates an error if a disallowed value is specified. - * While dataSource only allows local objects, - dataSourceRef allows objects in any namespaces. - (Beta) Using this field requires the AnyVolumeDataSource - feature gate to be enabled. (Alpha) Using the - namespace field of dataSourceRef requires the - CrossNamespaceVolumeDataSource feature gate to - be enabled.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API - group. For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - namespace: - description: Namespace is the namespace of resource - being referenced Note that when a namespace - is specified, a gateway.networking.k8s.io/ReferenceGrant - object is required in the referent namespace - to allow that namespace's owner to accept - the reference. See the ReferenceGrant documentation - for details. (Alpha) This field requires the - CrossNamespaceVolumeDataSource feature gate - to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify - resource requirements that are lower than previous - value but must still be higher than capacity recorded - in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used - by this container. \n This is an alpha field - and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. - It can only be set for containers." - items: - description: ResourceClaim references one - entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name - of one entry in pod.spec.resourceClaims - of the Pod where this field is used. - It makes that resource available inside - a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. If Requests - is omitted for a container, it defaults to - Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: selector is a label query over volumes - to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: 'storageClassName is the name of the - StorageClass required by the claim. More info: - https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines what type of volume - is required by the claim. Value of Filesystem - is implied when not included in claim spec. - type: string - volumeName: - description: volumeName is the binding reference - to the PersistentVolume backing this claim. - type: string - type: object - required: - - spec - type: object - type: object - fc: - description: fc represents a Fibre Channel resource that is - attached to a kubelet's host machine and then exposed to the - pod. - properties: - fsType: - description: 'fsType is the filesystem type to mount. Must - be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. TODO: how do we prevent errors in the - filesystem from compromising the machine' - type: string - lun: - description: 'lun is Optional: FC target lun number' - format: int32 - type: integer - readOnly: - description: 'readOnly is Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts.' - type: boolean - targetWWNs: - description: 'targetWWNs is Optional: FC target worldwide - names (WWNs)' - items: - type: string - type: array - wwids: - description: 'wwids Optional: FC volume world wide identifiers - (wwids) Either wwids or combination of targetWWNs and - lun must be set, but not both simultaneously.' - items: - type: string - type: array - type: object - flexVolume: - description: flexVolume represents a generic volume resource - that is provisioned/attached using an exec based plugin. - properties: - driver: - description: driver is the name of the driver to use for - this volume. - type: string - fsType: - description: fsType is the filesystem type to mount. Must - be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". The default filesystem depends - on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'options is Optional: this field holds extra - command options if any.' - type: object - readOnly: - description: 'readOnly is Optional: defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts.' - type: boolean - secretRef: - description: 'secretRef is Optional: secretRef is reference - to the secret object containing sensitive information - to pass to the plugin scripts. This may be empty if no - secret object is specified. If the secret object contains - more than one secret, all secrets are passed to the plugin - scripts.' - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - required: - - driver - type: object - flocker: - description: flocker represents a Flocker volume attached to - a kubelet's host machine. This depends on the Flocker control - service being running - properties: - datasetName: - description: datasetName is Name of the dataset stored as - metadata -> name on the dataset for Flocker should be - considered as deprecated - type: string - datasetUUID: - description: datasetUUID is the UUID of the dataset. This - is unique identifier of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: 'gcePersistentDisk represents a GCE Disk resource - that is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - properties: - fsType: - description: 'fsType is filesystem type of the volume that - you want to mount. Tip: Ensure that the filesystem type - is supported by the host operating system. Examples: "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from - compromising the machine' - type: string - partition: - description: 'partition is the partition in the volume that - you want to mount. If omitted, the default is to mount - by volume name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - format: int32 - type: integer - pdName: - description: 'pdName is unique name of the PD resource in - GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: string - readOnly: - description: 'readOnly here will force the ReadOnly setting - in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: boolean - required: - - pdName - type: object - gitRepo: - description: 'gitRepo represents a git repository at a particular - revision. DEPRECATED: GitRepo is deprecated. To provision - a container with a git repo, mount an EmptyDir into an InitContainer - that clones the repo using git, then mount the EmptyDir into - the Pod''s container.' - properties: - directory: - description: directory is the target directory name. Must - not contain or start with '..'. If '.' is supplied, the - volume directory will be the git repository. Otherwise, - if specified, the volume will contain the git repository - in the subdirectory with the given name. - type: string - repository: - description: repository is the URL - type: string - revision: - description: revision is the commit hash for the specified - revision. - type: string - required: - - repository - type: object - glusterfs: - description: 'glusterfs represents a Glusterfs mount on the - host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' - properties: - endpoints: - description: 'endpoints is the endpoint name that details - Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - path: - description: 'path is the Glusterfs volume path. More info: - https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - readOnly: - description: 'readOnly here will force the Glusterfs volume - to be mounted with read-only permissions. Defaults to - false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: 'hostPath represents a pre-existing file or directory - on the host machine that is directly exposed to the container. - This is generally used for system agents or other privileged - things that are allowed to see the host machine. Most containers - will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use host directory - mounts and who can/can not mount host directories as read/write.' - properties: - path: - description: 'path of the directory on the host. If the - path is a symlink, it will follow the link to the real - path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - type: - description: 'type for HostPath Volume Defaults to "" More - info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - required: - - path - type: object - iscsi: - description: 'iscsi represents an ISCSI Disk resource that is - attached to a kubelet''s host machine and then exposed to - the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' - properties: - chapAuthDiscovery: - description: chapAuthDiscovery defines whether support iSCSI - Discovery CHAP authentication - type: boolean - chapAuthSession: - description: chapAuthSession defines whether support iSCSI - Session CHAP authentication - type: boolean - fsType: - description: 'fsType is the filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from - compromising the machine' - type: string - initiatorName: - description: initiatorName is the custom iSCSI Initiator - Name. If initiatorName is specified with iscsiInterface - simultaneously, new iSCSI interface : will be created for the connection. - type: string - iqn: - description: iqn is the target iSCSI Qualified Name. - type: string - iscsiInterface: - description: iscsiInterface is the interface Name that uses - an iSCSI transport. Defaults to 'default' (tcp). - type: string - lun: - description: lun represents iSCSI Target Lun number. - format: int32 - type: integer - portals: - description: portals is the iSCSI Target Portal List. The - portal is either an IP or ip_addr:port if the port is - other than default (typically TCP ports 860 and 3260). - items: - type: string - type: array - readOnly: - description: readOnly here will force the ReadOnly setting - in VolumeMounts. Defaults to false. - type: boolean - secretRef: - description: secretRef is the CHAP Secret for iSCSI target - and initiator authentication - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - targetPortal: - description: targetPortal is iSCSI Target Portal. The Portal - is either an IP or ip_addr:port if the port is other than - default (typically TCP ports 860 and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: 'name of the volume. Must be a DNS_LABEL and unique - within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - nfs: - description: 'nfs represents an NFS mount on the host that shares - a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - properties: - path: - description: 'path that is exported by the NFS server. More - info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - readOnly: - description: 'readOnly here will force the NFS export to - be mounted with read-only permissions. Defaults to false. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: boolean - server: - description: 'server is the hostname or IP address of the - NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: 'persistentVolumeClaimVolumeSource represents a - reference to a PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - claimName: - description: 'claimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. More - info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - type: string - readOnly: - description: readOnly Will force the ReadOnly setting in - VolumeMounts. Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: photonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets host machine - properties: - fsType: - description: fsType is the filesystem type to mount. Must - be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. - type: string - pdID: - description: pdID is the ID that identifies Photon Controller - persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: portworxVolume represents a portworx volume attached - and mounted on kubelets host machine - properties: - fsType: - description: fSType represents the filesystem type to mount - Must be a filesystem type supported by the host operating - system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" - if unspecified. - type: string - readOnly: - description: readOnly defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - volumeID: - description: volumeID uniquely identifies a Portworx volume - type: string - required: - - volumeID - type: object - projected: - description: projected items for all in one resources secrets, - configmaps, and downward API - properties: - defaultMode: - description: defaultMode are the mode bits used to set permissions - on created files by default. Must be an octal value between - 0000 and 0777 or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON requires decimal - values for mode bits. Directories within the path are - not affected by this setting. This might be in conflict - with other options that affect the file mode, like fsGroup, - and the result can be other mode bits set. - format: int32 - type: integer - sources: - description: sources is the list of volume projections - items: - description: Projection that may be projected along with - other supported volume types - properties: - configMap: - description: configMap information about the configMap - data to project - properties: - items: - description: items if unspecified, each key-value - pair in the Data field of the referenced ConfigMap - will be projected into the volume as a file - whose name is the key and content is the value. - If specified, the listed keys will be projected - into the specified paths, and unlisted keys - will not be present. If a key is specified which - is not present in the ConfigMap, the volume - setup will error unless it is marked optional. - Paths must be relative and may not contain the - '..' path or start with '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode bits - used to set permissions on this file. - Must be an octal value between 0000 and - 0777 or a decimal value between 0 and - 511. YAML accepts both octal and decimal - values, JSON requires decimal values for - mode bits. If not specified, the volume - defaultMode will be used. This might be - in conflict with other options that affect - the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - path: - description: path is the relative path of - the file to map the key to. May not be - an absolute path. May not contain the - path element '..'. May not start with - the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: optional specify whether the ConfigMap - or its keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - downwardAPI: - description: downwardAPI information about the downwardAPI - data to project - properties: - items: - description: Items is a list of DownwardAPIVolume - file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing - the pod field - properties: - fieldRef: - description: 'Required: Selects a field - of the pod: only annotations, labels, - name and namespace are supported.' - properties: - apiVersion: - description: Version of the schema the - FieldPath is written in terms of, - defaults to "v1". - type: string - fieldPath: - description: Path of the field to select - in the specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - description: 'Optional: mode bits used to - set permissions on this file, must be - an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML - accepts both octal and decimal values, - JSON requires decimal values for mode - bits. If not specified, the volume defaultMode - will be used. This might be in conflict - with other options that affect the file - mode, like fsGroup, and the result can - be other mode bits set.' - format: int32 - type: integer - path: - description: 'Required: Path is the relative - path name of the file to be created. Must - not be absolute or contain the ''..'' - path. Must be utf-8 encoded. The first - item of the relative path must not start - with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource of the - container: only resources limits and requests - (limits.cpu, limits.memory, requests.cpu - and requests.memory) are currently supported.' - properties: - containerName: - description: 'Container name: required - for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format - of the exposed resources, defaults - to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to - select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - type: object - secret: - description: secret information about the secret data - to project - properties: - items: - description: items if unspecified, each key-value - pair in the Data field of the referenced Secret - will be projected into the volume as a file - whose name is the key and content is the value. - If specified, the listed keys will be projected - into the specified paths, and unlisted keys - will not be present. If a key is specified which - is not present in the Secret, the volume setup - will error unless it is marked optional. Paths - must be relative and may not contain the '..' - path or start with '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode bits - used to set permissions on this file. - Must be an octal value between 0000 and - 0777 or a decimal value between 0 and - 511. YAML accepts both octal and decimal - values, JSON requires decimal values for - mode bits. If not specified, the volume - defaultMode will be used. This might be - in conflict with other options that affect - the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - path: - description: path is the relative path of - the file to map the key to. May not be - an absolute path. May not contain the - path element '..'. May not start with - the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: optional field specify whether the - Secret or its key must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - serviceAccountToken: - description: serviceAccountToken is information about - the serviceAccountToken data to project - properties: - audience: - description: audience is the intended audience - of the token. A recipient of a token must identify - itself with an identifier specified in the audience - of the token, and otherwise should reject the - token. The audience defaults to the identifier - of the apiserver. - type: string - expirationSeconds: - description: expirationSeconds is the requested - duration of validity of the service account - token. As the token approaches expiration, the - kubelet volume plugin will proactively rotate - the service account token. The kubelet will - start trying to rotate the token if the token - is older than 80 percent of its time to live - or if the token is older than 24 hours.Defaults - to 1 hour and must be at least 10 minutes. - format: int64 - type: integer - path: - description: path is the path relative to the - mount point of the file to project the token - into. - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - description: quobyte represents a Quobyte mount on the host - that shares a pod's lifetime - properties: - group: - description: group to map volume access to Default is no - group - type: string - readOnly: - description: readOnly here will force the Quobyte volume - to be mounted with read-only permissions. Defaults to - false. - type: boolean - registry: - description: registry represents a single or multiple Quobyte - Registry services specified as a string as host:port pair - (multiple entries are separated with commas) which acts - as the central registry for volumes - type: string - tenant: - description: tenant owning the given Quobyte volume in the - Backend Used with dynamically provisioned Quobyte volumes, - value is set by the plugin - type: string - user: - description: user to map volume access to Defaults to serivceaccount - user - type: string - volume: - description: volume is a string that references an already - created Quobyte volume by name. - type: string - required: - - registry - - volume - type: object - rbd: - description: 'rbd represents a Rados Block Device mount on the - host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' - properties: - fsType: - description: 'fsType is the filesystem type of the volume - that you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from - compromising the machine' - type: string - image: - description: 'image is the rados image name. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - keyring: - description: 'keyring is the path to key ring for RBDUser. - Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - monitors: - description: 'monitors is a collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - items: - type: string - type: array - pool: - description: 'pool is the rados pool name. Default is rbd. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - readOnly: - description: 'readOnly here will force the ReadOnly setting - in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: boolean - secretRef: - description: 'secretRef is name of the authentication secret - for RBDUser. If provided overrides keyring. Default is - nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - user: - description: 'user is the rados user name. Default is admin. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - required: - - image - - monitors - type: object - scaleIO: - description: scaleIO represents a ScaleIO persistent volume - attached and mounted on Kubernetes nodes. - properties: - fsType: - description: fsType is the filesystem type to mount. Must - be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Default is "xfs". - type: string - gateway: - description: gateway is the host address of the ScaleIO - API Gateway. - type: string - protectionDomain: - description: protectionDomain is the name of the ScaleIO - Protection Domain for the configured storage. - type: string - readOnly: - description: readOnly Defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: secretRef references to the secret for ScaleIO - user and other sensitive information. If this is not provided, - Login operation will fail. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - sslEnabled: - description: sslEnabled Flag enable/disable SSL communication - with Gateway, default false - type: boolean - storageMode: - description: storageMode indicates whether the storage for - a volume should be ThickProvisioned or ThinProvisioned. - Default is ThinProvisioned. - type: string - storagePool: - description: storagePool is the ScaleIO Storage Pool associated - with the protection domain. - type: string - system: - description: system is the name of the storage system as - configured in ScaleIO. - type: string - volumeName: - description: volumeName is the name of a volume already - created in the ScaleIO system that is associated with - this volume source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: 'secret represents a secret that should populate - this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - properties: - defaultMode: - description: 'defaultMode is Optional: mode bits used to - set permissions on created files by default. Must be an - octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. Defaults to - 0644. Directories within the path are not affected by - this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - items: - description: items If unspecified, each key-value pair in - the Data field of the referenced Secret will be projected - into the volume as a file whose name is the key and content - is the value. If specified, the listed keys will be projected - into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in - the Secret, the volume setup will error unless it is marked - optional. Paths must be relative and may not contain the - '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: 'mode is Optional: mode bits used to - set permissions on this file. Must be an octal value - between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. If not - specified, the volume defaultMode will be used. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - path: - description: path is the relative path of the file - to map the key to. May not be an absolute path. - May not contain the path element '..'. May not start - with the string '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: optional field specify whether the Secret or - its keys must be defined - type: boolean - secretName: - description: 'secretName is the name of the secret in the - pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - type: string - type: object - storageos: - description: storageOS represents a StorageOS volume attached - and mounted on Kubernetes nodes. - properties: - fsType: - description: fsType is the filesystem type to mount. Must - be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. - type: string - readOnly: - description: readOnly defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: secretRef specifies the secret to use for obtaining - the StorageOS API credentials. If not specified, default - values will be attempted. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - volumeName: - description: volumeName is the human-readable name of the - StorageOS volume. Volume names are only unique within - a namespace. - type: string - volumeNamespace: - description: volumeNamespace specifies the scope of the - volume within StorageOS. If no namespace is specified - then the Pod's namespace will be used. This allows the - Kubernetes name scoping to be mirrored within StorageOS - for tighter integration. Set VolumeName to any name to - override the default behaviour. Set to "default" if you - are not using namespaces within StorageOS. Namespaces - that do not pre-exist within StorageOS will be created. - type: string - type: object - vsphereVolume: - description: vsphereVolume represents a vSphere volume attached - and mounted on kubelets host machine - properties: - fsType: - description: fsType is filesystem type to mount. Must be - a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. - type: string - storagePolicyID: - description: storagePolicyID is the storage Policy Based - Management (SPBM) profile ID associated with the StoragePolicyName. - type: string - storagePolicyName: - description: storagePolicyName is the storage Policy Based - Management (SPBM) profile name. - type: string - volumePath: - description: volumePath is the path that identifies vSphere - volume vmdk - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - web: - description: Defines the web command line flags when starting Alertmanager. - properties: - getConcurrency: - description: Maximum number of GET requests processed concurrently. - This corresponds to the Alertmanager's `--web.get-concurrency` - flag. - format: int32 - type: integer - httpConfig: - description: Defines HTTP parameters for web server. - properties: - headers: - description: List of headers that can be added to HTTP responses. - properties: - contentSecurityPolicy: - description: Set the Content-Security-Policy header to - HTTP responses. Unset if blank. - type: string - strictTransportSecurity: - description: Set the Strict-Transport-Security header - to HTTP responses. Unset if blank. Please make sure - that you use this with care as this header might force - browsers to load Prometheus and the other applications - hosted on the same domain and subdomains over HTTPS. - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security - type: string - xContentTypeOptions: - description: Set the X-Content-Type-Options header to - HTTP responses. Unset if blank. Accepted value is nosniff. - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options - enum: - - "" - - NoSniff - type: string - xFrameOptions: - description: Set the X-Frame-Options header to HTTP responses. - Unset if blank. Accepted values are deny and sameorigin. - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options - enum: - - "" - - Deny - - SameOrigin - type: string - xXSSProtection: - description: Set the X-XSS-Protection header to all responses. - Unset if blank. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection - type: string - type: object - http2: - description: Enable HTTP/2 support. Note that HTTP/2 is only - supported with TLS. When TLSConfig is not configured, HTTP/2 - will be disabled. Whenever the value of the field changes, - a rolling update will be triggered. - type: boolean - type: object - timeout: - description: Timeout for HTTP requests. This corresponds to the - Alertmanager's `--web.timeout` flag. - format: int32 - type: integer - tlsConfig: - description: Defines the TLS parameters for HTTPS. - properties: - cert: - description: Contains the TLS certificate for the server. - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cipherSuites: - description: 'List of supported cipher suites for TLS versions - up to TLS 1.2. If empty, Go default cipher suites are used. - Available cipher suites are documented in the go documentation: - https://golang.org/pkg/crypto/tls/#pkg-constants' - items: - type: string - type: array - client_ca: - description: Contains the CA certificate for client certificate - authentication to the server. - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientAuthType: - description: 'Server policy for client authentication. Maps - to ClientAuth Policies. For more detail on clientAuth options: - https://golang.org/pkg/crypto/tls/#ClientAuthType' - type: string - curvePreferences: - description: 'Elliptic curves that will be used in an ECDHE - handshake, in preference order. Available curves are documented - in the go documentation: https://golang.org/pkg/crypto/tls/#CurveID' - items: - type: string - type: array - keySecret: - description: Secret containing the TLS key for the server. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - maxVersion: - description: Maximum TLS version that is acceptable. Defaults - to TLS13. - type: string - minVersion: - description: Minimum TLS version that is acceptable. Defaults - to TLS12. - type: string - preferServerCipherSuites: - description: Controls whether the server selects the client's - most preferred cipher suite, or the server's most preferred - cipher suite. If true then the server's preference, as expressed - in the order of elements in cipherSuites, is used. - type: boolean - required: - - cert - - keySecret - type: object - type: object - type: object - status: - description: 'Most recent observed status of the Alertmanager cluster. - Read-only. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' - properties: - availableReplicas: - description: Total number of available pods (ready for at least minReadySeconds) - targeted by this Alertmanager cluster. - format: int32 - type: integer - conditions: - description: The current state of the Alertmanager object. - items: - description: Condition represents the state of the resources associated - with the Prometheus, Alertmanager or ThanosRuler resource. - properties: - lastTransitionTime: - description: lastTransitionTime is the time of the last update - to the current status property. - format: date-time - type: string - message: - description: Human-readable message indicating details for the - condition's last transition. - 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[].observedGeneration` - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - type: integer - reason: - description: Reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition being reported. - type: string - required: - - lastTransitionTime - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - paused: - description: Represents whether any actions on the underlying managed - objects are being performed. Only delete actions will be performed. - type: boolean - replicas: - description: Total number of non-terminated pods targeted by this - Alertmanager object (their labels match the selector). - format: int32 - type: integer - unavailableReplicas: - description: Total number of unavailable pods targeted by this Alertmanager - object. - format: int32 - type: integer - updatedReplicas: - description: Total number of non-terminated pods targeted by this - Alertmanager object that have the desired version spec. - format: int32 - type: integer - required: - - availableReplicas - - paused - - replicas - - unavailableReplicas - - updatedReplicas - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null diff --git a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_podmonitors.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_podmonitors.yaml deleted file mode 100644 index 76bf32903d..0000000000 --- a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_podmonitors.yaml +++ /dev/null @@ -1,684 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.11.1 - creationTimestamp: null - name: podmonitors.monitoring.coreos.com -spec: - group: monitoring.coreos.com - names: - categories: - - prometheus-operator - kind: PodMonitor - listKind: PodMonitorList - plural: podmonitors - shortNames: - - pmon - singular: podmonitor - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: PodMonitor defines monitoring for a set of pods. - 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: Specification of desired Pod selection for target discovery - by Prometheus. - properties: - attachMetadata: - description: Attaches node metadata to discovered targets. Requires - Prometheus v2.35.0 and above. - properties: - node: - description: When set to true, Prometheus must have permissions - to get Nodes. - type: boolean - type: object - jobLabel: - description: The label to use to retrieve the job name from. - type: string - labelLimit: - description: Per-scrape limit on number of labels that will be accepted - for a sample. Only valid in Prometheus versions 2.27.0 and newer. - format: int64 - type: integer - labelNameLengthLimit: - description: Per-scrape limit on length of labels name that will be - accepted for a sample. Only valid in Prometheus versions 2.27.0 - and newer. - format: int64 - type: integer - labelValueLengthLimit: - description: Per-scrape limit on length of labels value that will - be accepted for a sample. Only valid in Prometheus versions 2.27.0 - and newer. - format: int64 - type: integer - namespaceSelector: - description: Selector to select which namespaces the Endpoints objects - are discovered from. - properties: - any: - description: Boolean describing whether all namespaces are selected - in contrast to a list restricting them. - type: boolean - matchNames: - description: List of namespace names to select from. - items: - type: string - type: array - type: object - podMetricsEndpoints: - description: A list of endpoints allowed as part of this PodMonitor. - items: - description: PodMetricsEndpoint defines a scrapeable endpoint of - a Kubernetes Pod serving Prometheus metrics. - properties: - authorization: - description: Authorization section for this endpoint - properties: - credentials: - description: The secret's key that contains the credentials - of the request - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults to Bearer, - Basic will cause an error - type: string - type: object - basicAuth: - description: 'BasicAuth allow an endpoint to authenticate over - basic authentication. More info: https://prometheus.io/docs/operating/configuration/#endpoint' - properties: - password: - description: The secret in the service monitor namespace - that contains the password for authentication. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor namespace - that contains the username for authentication. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: Secret to mount to read bearer token for scraping - targets. The secret needs to be in the same namespace as the - pod monitor and accessible by the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - enableHttp2: - description: Whether to enable HTTP2. - type: boolean - filterRunning: - description: 'Drop pods that are not running. (Failed, Succeeded). - Enabled by default. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase' - type: boolean - followRedirects: - description: FollowRedirects configures whether scrape requests - follow HTTP 3xx redirects. - type: boolean - honorLabels: - description: HonorLabels chooses the metric's labels on collisions - with target labels. - type: boolean - honorTimestamps: - description: HonorTimestamps controls whether Prometheus respects - the timestamps present in scraped data. - type: boolean - interval: - description: Interval at which metrics should be scraped If - not specified Prometheus' global scrape interval is used. - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - metricRelabelings: - description: MetricRelabelConfigs to apply to samples before - ingestion. - items: - description: 'RelabelConfig allows dynamic rewriting of the - label set, being applied to samples before ingestion. It - defines ``-section of Prometheus - configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' - properties: - action: - default: replace - description: Action to perform based on regex matching. - Default is 'replace'. uppercase and lowercase actions - require Prometheus >= 2.36. - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - description: Modulus to take of the hash of the source - label values. - format: int64 - type: integer - regex: - description: Regular expression against which the extracted - value is matched. Default is '(.*)' - type: string - replacement: - description: Replacement value against which a regex replace - is performed if the regular expression matches. Regex - capture groups are available. Default is '$1' - type: string - separator: - description: Separator placed between concatenated source - label values. default is ';'. - type: string - sourceLabels: - description: The source labels select values from existing - labels. Their content is concatenated using the configured - separator and matched against the configured regular - expression for the replace, keep, and drop actions. - items: - description: LabelName is a valid Prometheus label name - which may only contain ASCII letters, numbers, as - well as underscores. - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - description: Label to which the resulting value is written - in a replace action. It is mandatory for replace actions. - Regex capture groups are available. - type: string - type: object - type: array - oauth2: - description: OAuth2 for the URL. Only valid in Prometheus versions - 2.27.0 and newer. - properties: - clientId: - description: The secret or configmap containing the OAuth2 - client id - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 client secret - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token URL - type: object - scopes: - description: OAuth2 scopes used for the token request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - params: - additionalProperties: - items: - type: string - type: array - description: Optional HTTP URL parameters - type: object - path: - description: HTTP path to scrape for metrics. If empty, Prometheus - uses the default value (e.g. `/metrics`). - type: string - port: - description: Name of the pod port this endpoint refers to. Mutually - exclusive with targetPort. - type: string - proxyUrl: - description: ProxyURL eg http://proxyserver:2195 Directs scrapes - to proxy through this endpoint. - type: string - relabelings: - description: 'RelabelConfigs to apply to samples before scraping. - Prometheus Operator automatically adds relabelings for a few - standard Kubernetes fields. The original scrape job''s name - is available via the `__tmp_prometheus_job_name` label. More - info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config' - items: - description: 'RelabelConfig allows dynamic rewriting of the - label set, being applied to samples before ingestion. It - defines ``-section of Prometheus - configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' - properties: - action: - default: replace - description: Action to perform based on regex matching. - Default is 'replace'. uppercase and lowercase actions - require Prometheus >= 2.36. - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - description: Modulus to take of the hash of the source - label values. - format: int64 - type: integer - regex: - description: Regular expression against which the extracted - value is matched. Default is '(.*)' - type: string - replacement: - description: Replacement value against which a regex replace - is performed if the regular expression matches. Regex - capture groups are available. Default is '$1' - type: string - separator: - description: Separator placed between concatenated source - label values. default is ';'. - type: string - sourceLabels: - description: The source labels select values from existing - labels. Their content is concatenated using the configured - separator and matched against the configured regular - expression for the replace, keep, and drop actions. - items: - description: LabelName is a valid Prometheus label name - which may only contain ASCII letters, numbers, as - well as underscores. - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - description: Label to which the resulting value is written - in a replace action. It is mandatory for replace actions. - Regex capture groups are available. - type: string - type: object - type: array - scheme: - description: HTTP scheme to use for scraping. `http` and `https` - are the expected values unless you rewrite the `__scheme__` - label via relabeling. If empty, Prometheus uses the default - value `http`. - enum: - - http - - https - type: string - scrapeTimeout: - description: Timeout after which the scrape is ended If not - specified, the Prometheus global scrape interval is used. - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - targetPort: - anyOf: - - type: integer - - type: string - description: 'Deprecated: Use ''port'' instead.' - x-kubernetes-int-or-string: true - tlsConfig: - description: TLS configuration to use when scraping the endpoint. - properties: - ca: - description: Certificate authority used when verifying server - certificates. - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key file for the - targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the targets. - type: string - type: object - type: object - type: array - podTargetLabels: - description: PodTargetLabels transfers labels on the Kubernetes Pod - onto the target. - items: - type: string - type: array - sampleLimit: - description: SampleLimit defines per-scrape limit on number of scraped - samples that will be accepted. - format: int64 - type: integer - selector: - description: Selector to select Pod objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the key - and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship to - a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - targetLimit: - description: TargetLimit defines a limit on the number of scraped - targets that will be accepted. - format: int64 - type: integer - required: - - podMetricsEndpoints - - selector - type: object - required: - - spec - type: object - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null diff --git a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_probes.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_probes.yaml deleted file mode 100644 index 6f1163707a..0000000000 --- a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_probes.yaml +++ /dev/null @@ -1,727 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.11.1 - creationTimestamp: null - name: probes.monitoring.coreos.com -spec: - group: monitoring.coreos.com - names: - categories: - - prometheus-operator - kind: Probe - listKind: ProbeList - plural: probes - shortNames: - - prb - singular: probe - scope: Namespaced - versions: - - name: v1 - schema: - openAPIV3Schema: - description: Probe defines monitoring for a set of static targets or ingresses. - 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: Specification of desired Ingress selection for target discovery - by Prometheus. - properties: - authorization: - description: Authorization section for this endpoint - properties: - credentials: - description: The secret's key that contains the credentials of - the request - properties: - key: - description: The key of the secret to select from. Must be - a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be - defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: - description: Set the authentication type. Defaults to Bearer, - Basic will cause an error - type: string - type: object - basicAuth: - description: 'BasicAuth allow an endpoint to authenticate over basic - authentication. More info: https://prometheus.io/docs/operating/configuration/#endpoint' - properties: - password: - description: The secret in the service monitor namespace that - contains the password for authentication. - properties: - key: - description: The key of the secret to select from. Must be - a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be - defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor namespace that - contains the username for authentication. - properties: - key: - description: The key of the secret to select from. Must be - a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be - defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerTokenSecret: - description: Secret to mount to read bearer token for scraping targets. - The secret needs to be in the same namespace as the probe and accessible - by the Prometheus Operator. - properties: - key: - description: The key of the secret to select from. Must be a - valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - interval: - description: Interval at which targets are probed using the configured - prober. If not specified Prometheus' global scrape interval is used. - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - jobName: - description: The job name assigned to scraped metrics by default. - type: string - labelLimit: - description: Per-scrape limit on number of labels that will be accepted - for a sample. Only valid in Prometheus versions 2.27.0 and newer. - format: int64 - type: integer - labelNameLengthLimit: - description: Per-scrape limit on length of labels name that will be - accepted for a sample. Only valid in Prometheus versions 2.27.0 - and newer. - format: int64 - type: integer - labelValueLengthLimit: - description: Per-scrape limit on length of labels value that will - be accepted for a sample. Only valid in Prometheus versions 2.27.0 - and newer. - format: int64 - type: integer - metricRelabelings: - description: MetricRelabelConfigs to apply to samples before ingestion. - items: - description: 'RelabelConfig allows dynamic rewriting of the label - set, being applied to samples before ingestion. It defines ``-section - of Prometheus configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' - properties: - action: - default: replace - description: Action to perform based on regex matching. Default - is 'replace'. uppercase and lowercase actions require Prometheus - >= 2.36. - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - description: Modulus to take of the hash of the source label - values. - format: int64 - type: integer - regex: - description: Regular expression against which the extracted - value is matched. Default is '(.*)' - type: string - replacement: - description: Replacement value against which a regex replace - is performed if the regular expression matches. Regex capture - groups are available. Default is '$1' - type: string - separator: - description: Separator placed between concatenated source label - values. default is ';'. - type: string - sourceLabels: - description: The source labels select values from existing labels. - Their content is concatenated using the configured separator - and matched against the configured regular expression for - the replace, keep, and drop actions. - items: - description: LabelName is a valid Prometheus label name which - may only contain ASCII letters, numbers, as well as underscores. - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - description: Label to which the resulting value is written in - a replace action. It is mandatory for replace actions. Regex - capture groups are available. - type: string - type: object - type: array - module: - description: 'The module to use for probing specifying how to probe - the target. Example module configuring in the blackbox exporter: - https://github.com/prometheus/blackbox_exporter/blob/master/example.yml' - type: string - oauth2: - description: OAuth2 for the URL. Only valid in Prometheus versions - 2.27.0 and newer. - properties: - clientId: - description: The secret or configmap containing the OAuth2 client - id - properties: - configMap: - description: ConfigMap containing data to use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - clientSecret: - description: The secret containing the OAuth2 client secret - properties: - key: - description: The key of the secret to select from. Must be - a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be - defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - endpointParams: - additionalProperties: - type: string - description: Parameters to append to the token URL - type: object - scopes: - description: OAuth2 scopes used for the token request - items: - type: string - type: array - tokenUrl: - description: The URL to fetch the token from - minLength: 1 - type: string - required: - - clientId - - clientSecret - - tokenUrl - type: object - prober: - description: Specification for the prober to use for probing targets. - The prober.URL parameter is required. Targets cannot be probed if - left empty. - properties: - path: - default: /probe - description: Path to collect metrics from. Defaults to `/probe`. - type: string - proxyUrl: - description: Optional ProxyURL. - type: string - scheme: - description: HTTP scheme to use for scraping. `http` and `https` - are the expected values unless you rewrite the `__scheme__` - label via relabeling. If empty, Prometheus uses the default - value `http`. - enum: - - http - - https - type: string - url: - description: Mandatory URL of the prober. - type: string - required: - - url - type: object - sampleLimit: - description: SampleLimit defines per-scrape limit on number of scraped - samples that will be accepted. - format: int64 - type: integer - scrapeTimeout: - description: Timeout for scraping metrics from the Prometheus exporter. - If not specified, the Prometheus global scrape timeout is used. - pattern: ^(0|(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?)$ - type: string - targetLimit: - description: TargetLimit defines a limit on the number of scraped - targets that will be accepted. - format: int64 - type: integer - targets: - description: Targets defines a set of static or dynamically discovered - targets to probe. - properties: - ingress: - description: ingress defines the Ingress objects to probe and - the relabeling configuration. If `staticConfig` is also defined, - `staticConfig` takes precedence. - properties: - namespaceSelector: - description: From which namespaces to select Ingress objects. - properties: - any: - description: Boolean describing whether all namespaces - are selected in contrast to a list restricting them. - type: boolean - matchNames: - description: List of namespace names to select from. - items: - type: string - type: array - type: object - relabelingConfigs: - description: 'RelabelConfigs to apply to the label set of - the target before it gets scraped. The original ingress - address is available via the `__tmp_prometheus_ingress_address` - label. It can be used to customize the probed URL. The original - scrape job''s name is available via the `__tmp_prometheus_job_name` - label. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config' - items: - description: 'RelabelConfig allows dynamic rewriting of - the label set, being applied to samples before ingestion. - It defines ``-section of Prometheus - configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' - properties: - action: - default: replace - description: Action to perform based on regex matching. - Default is 'replace'. uppercase and lowercase actions - require Prometheus >= 2.36. - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - description: Modulus to take of the hash of the source - label values. - format: int64 - type: integer - regex: - description: Regular expression against which the extracted - value is matched. Default is '(.*)' - type: string - replacement: - description: Replacement value against which a regex - replace is performed if the regular expression matches. - Regex capture groups are available. Default is '$1' - type: string - separator: - description: Separator placed between concatenated source - label values. default is ';'. - type: string - sourceLabels: - description: The source labels select values from existing - labels. Their content is concatenated using the configured - separator and matched against the configured regular - expression for the replace, keep, and drop actions. - items: - description: LabelName is a valid Prometheus label - name which may only contain ASCII letters, numbers, - as well as underscores. - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - description: Label to which the resulting value is written - in a replace action. It is mandatory for replace actions. - Regex capture groups are available. - type: string - type: object - type: array - selector: - description: Selector to select the Ingress objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, - Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists or - DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: object - staticConfig: - description: 'staticConfig defines the static list of targets - to probe and the relabeling configuration. If `ingress` is also - defined, `staticConfig` takes precedence. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#static_config.' - properties: - labels: - additionalProperties: - type: string - description: Labels assigned to all metrics scraped from the - targets. - type: object - relabelingConfigs: - description: 'RelabelConfigs to apply to the label set of - the targets before it gets scraped. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config' - items: - description: 'RelabelConfig allows dynamic rewriting of - the label set, being applied to samples before ingestion. - It defines ``-section of Prometheus - configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' - properties: - action: - default: replace - description: Action to perform based on regex matching. - Default is 'replace'. uppercase and lowercase actions - require Prometheus >= 2.36. - enum: - - replace - - Replace - - keep - - Keep - - drop - - Drop - - hashmod - - HashMod - - labelmap - - LabelMap - - labeldrop - - LabelDrop - - labelkeep - - LabelKeep - - lowercase - - Lowercase - - uppercase - - Uppercase - - keepequal - - KeepEqual - - dropequal - - DropEqual - type: string - modulus: - description: Modulus to take of the hash of the source - label values. - format: int64 - type: integer - regex: - description: Regular expression against which the extracted - value is matched. Default is '(.*)' - type: string - replacement: - description: Replacement value against which a regex - replace is performed if the regular expression matches. - Regex capture groups are available. Default is '$1' - type: string - separator: - description: Separator placed between concatenated source - label values. default is ';'. - type: string - sourceLabels: - description: The source labels select values from existing - labels. Their content is concatenated using the configured - separator and matched against the configured regular - expression for the replace, keep, and drop actions. - items: - description: LabelName is a valid Prometheus label - name which may only contain ASCII letters, numbers, - as well as underscores. - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - type: string - type: array - targetLabel: - description: Label to which the resulting value is written - in a replace action. It is mandatory for replace actions. - Regex capture groups are available. - type: string - type: object - type: array - static: - description: The list of hosts to probe. - items: - type: string - type: array - type: object - type: object - tlsConfig: - description: TLS configuration to use when scraping the endpoint. - properties: - ca: - description: Certificate authority used when verifying server - certificates. - properties: - configMap: - description: ConfigMap containing data to use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - cert: - description: Client certificate to present when doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to use for the targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keySecret: - description: Secret containing the client key file for the targets. - properties: - key: - description: The key of the secret to select from. Must be - a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be - defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the targets. - type: string - type: object - type: object - required: - - spec - type: object - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null diff --git a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheusagents.yaml b/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheusagents.yaml deleted file mode 100644 index 848b223b5d..0000000000 --- a/testdata/images/bundles/prometheus-operator/v1.0.0/manifests/monitoring.coreos.com_prometheusagents.yaml +++ /dev/null @@ -1,8022 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.11.1 - creationTimestamp: null - name: prometheusagents.monitoring.coreos.com -spec: - group: monitoring.coreos.com - names: - categories: - - prometheus-operator - kind: PrometheusAgent - listKind: PrometheusAgentList - plural: prometheusagents - shortNames: - - promagent - singular: prometheusagent - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The version of Prometheus agent - jsonPath: .spec.version - name: Version - type: string - - description: The number of desired replicas - jsonPath: .spec.replicas - name: Desired - type: integer - - description: The number of ready replicas - jsonPath: .status.availableReplicas - name: Ready - type: integer - - jsonPath: .status.conditions[?(@.type == 'Reconciled')].status - name: Reconciled - type: string - - jsonPath: .status.conditions[?(@.type == 'Available')].status - name: Available - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Whether the resource reconciliation is paused or not - jsonPath: .status.paused - name: Paused - priority: 1 - type: boolean - name: v1alpha1 - schema: - openAPIV3Schema: - description: PrometheusAgent defines a Prometheus agent deployment. - 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: 'Specification of the desired behavior of the Prometheus - agent. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' - properties: - additionalArgs: - description: AdditionalArgs allows setting additional arguments for - the Prometheus container. It is intended for e.g. activating hidden - flags which are not supported by the dedicated configuration options - yet. The arguments are passed as-is to the Prometheus container - which may cause issues if they are invalid or not supported by the - given Prometheus version. In case of an argument conflict (e.g. - an argument which is already set by the operator itself) or when - providing an invalid argument the reconciliation will fail and an - error will be logged. - items: - description: Argument as part of the AdditionalArgs list. - properties: - name: - description: Name of the argument, e.g. "scrape.discovery-reload-interval". - minLength: 1 - type: string - value: - description: Argument value, e.g. 30s. Can be empty for name-only - arguments (e.g. --storage.tsdb.no-lockfile) - type: string - required: - - name - type: object - type: array - additionalScrapeConfigs: - description: 'AdditionalScrapeConfigs allows specifying a key of a - Secret containing additional Prometheus scrape configurations. Scrape - configurations specified are appended to the configurations generated - by the Prometheus Operator. Job configurations specified must have - the form as specified in the official Prometheus documentation: - https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config. - As scrape configs are appended, the user is responsible to make - sure it is valid. Note that using this feature may expose the possibility - to break upgrades of Prometheus. It is advised to review Prometheus - release notes to ensure that no incompatible scrape configs are - going to break Prometheus after the upgrade.' - properties: - key: - description: The key of the secret to select from. Must be a - valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - affinity: - description: If specified, the pod's scheduling constraints. - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for the - pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods to - nodes that satisfy the affinity expressions specified by - this field, but it may choose a node that violates one or - more of the expressions. The node that is most preferred - is the one with the greatest sum of weights, i.e. for each - node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements of - this field and adding "weight" to the sum if the node matches - the corresponding matchExpressions; the node(s) with the - highest sum are the most preferred. - items: - description: An empty preferred scheduling term matches - all objects with implicit weight 0 (i.e. it's a no-op). - A null preferred scheduling term matches no objects (i.e. - is also a no-op). - properties: - preference: - description: A node selector term, associated with the - corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the corresponding - nodeSelectorTerm, in the range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by this - field are not met at scheduling time, the pod will not be - scheduled onto the node. If the affinity requirements specified - by this field cease to be met at some point during pod execution - (e.g. due to an update), the system may or may not try to - eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. - The terms are ORed. - items: - description: A null or empty node selector term matches - no objects. The requirements of them are ANDed. The - TopologySelectorTerm type implements a subset of the - NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string values. If - the operator is In or NotIn, the values - array must be non-empty. If the operator - is Exists or DoesNotExist, the values array - must be empty. If the operator is Gt or - Lt, the values array must have a single - element, which will be interpreted as an - integer. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - x-kubernetes-map-type: atomic - type: array - required: - - nodeSelectorTerms - type: object - x-kubernetes-map-type: atomic - type: object - podAffinity: - description: Describes pod affinity scheduling rules (e.g. co-locate - this pod in the same node, zone, etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods to - nodes that satisfy the affinity expressions specified by - this field, but it may choose a node that violates one or - more of the expressions. The node that is most preferred - is the one with the greatest sum of weights, i.e. for each - node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, - etc.), compute a sum by iterating through the elements of - this field and adding "weight" to the sum if the node has - pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching the corresponding - podAffinityTerm, in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by this - field are not met at scheduling time, the pod will not be - scheduled onto the node. If the affinity requirements specified - by this field cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may or may - not try to eventually evict the pod from its node. When - there are multiple elements, the lists of nodes corresponding - to each podAffinityTerm are intersected, i.e. all terms - must be satisfied. - items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not co-located - (anti-affinity) with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which a pod of the set of - pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied to the - union of the namespaces selected by this field and - the ones listed in the namespaces field. null selector - and null or empty namespaces list means "this pod's - namespace". An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static list of namespace - names that the term applies to. The term is applied - to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. null or - empty namespaces list and null namespaceSelector means - "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where - co-located is defined as running on a node whose value - of the label with key topologyKey matches that of - any node on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules (e.g. - avoid putting this pod in the same node, zone, etc. as some - other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods to - nodes that satisfy the anti-affinity expressions specified - by this field, but it may choose a node that violates one - or more of the expressions. The node that is most preferred - is the one with the greatest sum of weights, i.e. for each - node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, - etc.), compute a sum by iterating through the elements of - this field and adding "weight" to the sum if the node has - pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching the corresponding - podAffinityTerm, in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the anti-affinity requirements - specified by this field cease to be met at some point during - pod execution (e.g. due to a pod label update), the system - may or may not try to eventually evict the pod from its - node. When there are multiple elements, the lists of nodes - corresponding to each podAffinityTerm are intersected, i.e. - all terms must be satisfied. - items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not co-located - (anti-affinity) with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which a pod of the set of - pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied to the - union of the namespaces selected by this field and - the ones listed in the namespaces field. null selector - and null or empty namespaces list means "this pod's - namespace". An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a - selector that contains values, a key, and an - operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If the - operator is Exists or DoesNotExist, the - values array must be empty. This array is - replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". The - requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static list of namespace - names that the term applies to. The term is applied - to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. null or - empty namespaces list and null namespaceSelector means - "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where - co-located is defined as running on a node whose value - of the label with key topologyKey matches that of - any node on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - apiserverConfig: - description: APIServerConfig allows specifying a host and auth methods - to access apiserver. If left empty, Prometheus is assumed to run - inside of the cluster and will discover API servers automatically - and use the pod's CA certificate and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/. - properties: - authorization: - description: Authorization section for accessing apiserver - properties: - credentials: - description: The secret's key that contains the credentials - of the request - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - credentialsFile: - description: File to read a secret from, mutually exclusive - with Credentials (from SafeAuthorization) - type: string - type: - description: Set the authentication type. Defaults to Bearer, - Basic will cause an error - type: string - type: object - basicAuth: - description: BasicAuth allow an endpoint to authenticate over - basic authentication - properties: - password: - description: The secret in the service monitor namespace that - contains the password for authentication. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - username: - description: The secret in the service monitor namespace that - contains the username for authentication. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - bearerToken: - description: Bearer token for accessing apiserver. - type: string - bearerTokenFile: - description: File to read bearer token for accessing apiserver. - type: string - host: - description: Host of apiserver. A valid string consisting of a - hostname or IP followed by an optional port number - type: string - tlsConfig: - description: TLS Config to use for accessing apiserver. - properties: - ca: - description: Certificate authority used when verifying server - certificates. - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - caFile: - description: Path to the CA cert in the Prometheus container - to use for the targets. - type: string - cert: - description: Client certificate to present when doing client-authentication. - properties: - configMap: - description: ConfigMap containing data to use for the - targets. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - secret: - description: Secret containing data to use for the targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - certFile: - description: Path to the client cert file in the Prometheus - container for the targets. - type: string - insecureSkipVerify: - description: Disable target certificate validation. - type: boolean - keyFile: - description: Path to the client key file in the Prometheus - container for the targets. - type: string - keySecret: - description: Secret containing the client key file for the - targets. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - serverName: - description: Used to verify the hostname for the targets. - type: string - type: object - required: - - host - type: object - arbitraryFSAccessThroughSMs: - description: ArbitraryFSAccessThroughSMs configures whether configuration - based on a service monitor can access arbitrary files on the file - system of the Prometheus container e.g. bearer token files. - properties: - deny: - type: boolean - type: object - configMaps: - description: ConfigMaps is a list of ConfigMaps in the same namespace - as the Prometheus object, which shall be mounted into the Prometheus - Pods. Each ConfigMap is added to the StatefulSet definition as a - volume named `configmap-`. The ConfigMaps are mounted - into /etc/prometheus/configmaps/ in the 'prometheus' - container. - items: - type: string - type: array - containers: - description: 'Containers allows injecting additional containers or - modifying operator generated containers. This can be used to allow - adding an authentication proxy to a Prometheus pod or to change - the behavior of an operator generated container. Containers described - here modify an operator generated container if they share the same - name and modifications are done via a strategic merge patch. The - current container names are: `prometheus`, `config-reloader`, and - `thanos-sidecar`. Overriding containers is entirely outside the - scope of what the maintainers will support and by doing so, you - accept that this behaviour may break at any time without notice.' - items: - description: A single application container that you want to run - within a pod. - properties: - args: - description: 'Arguments to the entrypoint. The container image''s - CMD is used if this is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. If a variable - cannot be resolved, the reference in the input string will - be unchanged. Double $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will - produce the string literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed within a shell. - The container image''s ENTRYPOINT is used if this is not provided. - Variable references $(VAR_NAME) are expanded using the container''s - environment. If a variable cannot be resolved, the reference - in the input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) syntax: - i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless of whether - the variable exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables to set in the container. - Cannot be updated. - items: - description: EnvVar represents an environment variable present - in a Container. - properties: - name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in - the container and any service environment variables. - If a variable cannot be resolved, the reference in the - input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) - syntax: i.e. "$$(VAR_NAME)" will produce the string - literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists - or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. - Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: 'Selects a field of the pod: supports - metadata.name, metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the - specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, requests.cpu, - requests.memory and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the - exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's - namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment variables - in the container. The keys defined within a source must be - a C_IDENTIFIER. All invalid keys will be reported as an event - when the container is starting. When a key exists in multiple - sources, the value associated with the last source will take - precedence. Values defined by an Env with a duplicate key - will take precedence. Cannot be updated. - items: - description: EnvFromSource represents the source of a set - of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap must be - defined - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - type: object - type: array - image: - description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level config management - to default or override container images in workload controllers - like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, Never, IfNotPresent. - Defaults to Always if :latest tag is specified, or IfNotPresent - otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management system should take - in response to container lifecycle events. Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately after a container - is created. If the handler fails, the container is terminated - and restarted according to its restart policy. Other management - of the container blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for - the command is root ('/') in the container's - filesystem. The command is simply exec'd, it is - not run inside a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you need - to explicitly call out to that shell. Exit status - of 0 is treated as live/healthy and non-zero is - unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to - the pod IP. You probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the request. - HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the - host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward compatibility. - There are no validation of this field and lifecycle - hooks will fail in runtime when tcp handler is specified. - properties: - host: - description: 'Optional: Host name to connect to, - defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately before a container - is terminated due to an API request or management event - such as liveness/startup probe failure, preemption, resource - contention, etc. The handler is not called if the container - crashes or exits. The Pod''s termination grace period - countdown begins before the PreStop hook is executed. - Regardless of the outcome of the handler, the container - will eventually terminate within the Pod''s termination - grace period (unless delayed by finalizers). Other management - of the container blocks until the hook completes or until - the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for - the command is root ('/') in the container's - filesystem. The command is simply exec'd, it is - not run inside a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you need - to explicitly call out to that shell. Exit status - of 0 is treated as live/healthy and non-zero is - unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to - the pod IP. You probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the request. - HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the - host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward compatibility. - There are no validation of this field and lifecycle - hooks will fail in runtime when tcp handler is specified. - properties: - host: - description: 'Optional: Host name to connect to, - defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container liveness. Container - will be restarted if the probe fails. Cannot be updated. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified as a DNS_LABEL. - Each container in a pod must have a unique name (DNS_LABEL). - Cannot be updated. - type: string - ports: - description: List of ports to expose from the container. Not - specifying a port here DOES NOT prevent that port from being - exposed. Any port which is listening on the default "0.0.0.0" - address inside a container will be accessible from the network. - Modifying this array with strategic merge patch may corrupt - the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. - Cannot be updated. - items: - description: ContainerPort represents a network port in a - single container. - properties: - containerPort: - description: Number of port to expose on the pod's IP - address. This must be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external port to. - type: string - hostPort: - description: Number of port to expose on the host. If - specified, this must be a valid port number, 0 < x < - 65536. If HostNetwork is specified, this must match - ContainerPort. Most containers do not need this. - format: int32 - type: integer - name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port in a pod - must have a unique name. Name for the port that can - be referred to by services. - type: string - protocol: - default: TCP - description: Protocol for port. Must be UDP, TCP, or SCTP. - Defaults to "TCP". - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container service readiness. - Container will be removed from service endpoints if the probe - fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resizePolicy: - description: Resources resize policy for the container. - items: - description: ContainerResizePolicy represents resource resize - policy for the container. - properties: - resourceName: - description: 'Name of the resource to which this resource - resize policy applies. Supported values: cpu, memory.' - type: string - restartPolicy: - description: Restart policy to apply when specified resource - is resized. If not specified, it defaults to NotRequired. - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - description: 'Compute Resources required by this container. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: 'SecurityContext defines the security options the - container should be run with. If set, the fields of SecurityContext - override the equivalent fields of PodSecurityContext. More - info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether - a process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by - the container runtime. Note that this field cannot be - set when spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes - in privileged containers are essentially equivalent to - root on the host. Defaults to false. Note that this field - cannot be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to - use for the containers. The default is DefaultProcMount - which uses the container runtime defaults for readonly - paths and masked paths. This requires the ProcMountType - feature flag to be enabled. Note that this field cannot - be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root - filesystem. Default is false. Note that this field cannot - be set when spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a - non-root user. If true, the Kubelet will validate the - image at runtime to ensure that it does not run as UID - 0 (root) and fail to start the container if it does. If - unset or false, no such validation will be performed. - May also be set in PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a - random SELinux context for each container. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. - Note that this field cannot be set when spec.os.name is - windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile - must be preconfigured on the node to work. Must be - a descending path, relative to the kubelet's configured - seccomp profile location. Must only be set if type - is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - - a profile defined in a file on the node should be - used. RuntimeDefault - the container runtime default - profile should be used. Unconfined - no profile should - be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the - GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components - that enable the WindowsHostProcessContainers feature - flag. Setting this field without the feature flag - will result in errors when validating the Pod. All - of a Pod's containers must have the same effective - HostProcess value (it is not allowed to have a mix - of HostProcess containers and non-HostProcess containers). In - addition, if HostProcess is true then HostNetwork - must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that the Pod has successfully - initialized. If specified, no other probes are executed until - this completes successfully. If this probe fails, the Pod - will be restarted, just as if the livenessProbe failed. This - can be used to provide different probe parameters at the beginning - of a Pod''s lifecycle, when it might take a long time to load - data or warm a cache, than during steady-state operation. - This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should allocate a buffer - for stdin in the container runtime. If this is not set, reads - from stdin in the container will always result in EOF. Default - is false. - type: boolean - stdinOnce: - description: Whether the container runtime should close the - stdin channel after it has been opened by a single attach. - When stdin is true the stdin stream will remain open across - multiple attach sessions. If stdinOnce is set to true, stdin - is opened on container start, is empty until the first client - attaches to stdin, and then remains open and accepts data - until the client disconnects, at which time stdin is closed - and remains closed until the container is restarted. If this - flag is false, a container processes that reads from stdin - will never receive an EOF. Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the file to which the - container''s termination message will be written is mounted - into the container''s filesystem. Message written is intended - to be brief final status, such as an assertion failure message. - Will be truncated by the node if greater than 4096 bytes. - The total message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination message should be - populated. File will use the contents of terminationMessagePath - to populate the container status message on both success and - failure. FallbackToLogsOnError will use the last chunk of - container log output if the termination message file is empty - and the container exited with an error. The log output is - limited to 2048 bytes or 80 lines, whichever is smaller. Defaults - to File. Cannot be updated. - type: string - tty: - description: Whether this container should allocate a TTY for - itself, also requires 'stdin' to be true. Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of block devices to be - used by the container. - items: - description: volumeDevice describes a mapping of a raw block - device within a container. - properties: - devicePath: - description: devicePath is the path inside of the container - that the device will be mapped to. - type: string - name: - description: name must match the name of a persistentVolumeClaim - in the pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the container's filesystem. - Cannot be updated. - items: - description: VolumeMount describes a mounting of a Volume - within a container. - properties: - mountPath: - description: Path within the container at which the volume - should be mounted. Must not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines how mounts are - propagated from the host to container and the other - way around. When not set, MountPropagationNone is used. - This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write otherwise - (false or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume from which the container's - volume should be mounted. Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves similarly - to SubPath but environment variable references $(VAR_NAME) - are expanded using the container's environment. Defaults - to "" (volume's root). SubPathExpr and SubPath are mutually - exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. If not specified, - the container runtime's default will be used, which might - be configured in the container image. Cannot be updated. - type: string - required: - - name - type: object - type: array - enableFeatures: - description: Enable access to Prometheus disabled features. By default, - no features are enabled. Enabling disabled features is entirely - outside the scope of what the maintainers will support and by doing - so, you accept that this behaviour may break at any time without - notice. For more information see https://prometheus.io/docs/prometheus/latest/disabled_features/ - items: - type: string - type: array - enableRemoteWriteReceiver: - description: 'Enable Prometheus to be used as a receiver for the Prometheus - remote write protocol. Defaults to the value of `false`. WARNING: - This is not considered an efficient way of ingesting samples. Use - it with caution for specific low-volume use cases. It is not suitable - for replacing the ingestion via scraping and turning Prometheus - into a push-based metrics collection system. For more information - see https://prometheus.io/docs/prometheus/latest/querying/api/#remote-write-receiver - Only valid in Prometheus versions 2.33.0 and newer.' - type: boolean - enforcedBodySizeLimit: - description: 'EnforcedBodySizeLimit defines the maximum size of uncompressed - response body that will be accepted by Prometheus. Targets responding - with a body larger than this many bytes will cause the scrape to - fail. Example: 100MB. If defined, the limit will apply to all service/pod - monitors and probes. This is an experimental feature, this behaviour - could change or be removed in the future. Only valid in Prometheus - versions 2.28.0 and newer.' - pattern: (^0|([0-9]*[.])?[0-9]+((K|M|G|T|E|P)i?)?B)$ - type: string - enforcedLabelLimit: - description: Per-scrape limit on number of labels that will be accepted - for a sample. If more than this number of labels are present post - metric-relabeling, the entire scrape will be treated as failed. - 0 means no limit. Only valid in Prometheus versions 2.27.0 and newer. - format: int64 - type: integer - enforcedLabelNameLengthLimit: - description: Per-scrape limit on length of labels name that will be - accepted for a sample. If a label name is longer than this number - post metric-relabeling, the entire scrape will be treated as failed. - 0 means no limit. Only valid in Prometheus versions 2.27.0 and newer. - format: int64 - type: integer - enforcedLabelValueLengthLimit: - description: Per-scrape limit on length of labels value that will - be accepted for a sample. If a label value is longer than this number - post metric-relabeling, the entire scrape will be treated as failed. - 0 means no limit. Only valid in Prometheus versions 2.27.0 and newer. - format: int64 - type: integer - enforcedNamespaceLabel: - description: "EnforcedNamespaceLabel If set, a label will be added - to \n 1. all user-metrics (created by `ServiceMonitor`, `PodMonitor` - and `Probe` objects) and 2. in all `PrometheusRule` objects (except - the ones excluded in `prometheusRulesExcludedFromEnforce`) to * - alerting & recording rules and * the metrics used in their expressions - (`expr`). \n Label name is this field's value. Label value is the - namespace of the created object (mentioned above)." - type: string - enforcedSampleLimit: - description: EnforcedSampleLimit defines global limit on number of - scraped samples that will be accepted. This overrides any SampleLimit - set per ServiceMonitor or/and PodMonitor. It is meant to be used - by admins to enforce the SampleLimit to keep overall number of samples/series - under the desired limit. Note that if SampleLimit is lower that - value will be taken instead. - format: int64 - type: integer - enforcedTargetLimit: - description: EnforcedTargetLimit defines a global limit on the number - of scraped targets. This overrides any TargetLimit set per ServiceMonitor - or/and PodMonitor. It is meant to be used by admins to enforce - the TargetLimit to keep the overall number of targets under the - desired limit. Note that if TargetLimit is lower, that value will - be taken instead, except if either value is zero, in which case - the non-zero value will be used. If both values are zero, no limit - is enforced. - format: int64 - type: integer - excludedFromEnforcement: - description: List of references to PodMonitor, ServiceMonitor, Probe - and PrometheusRule objects to be excluded from enforcing a namespace - label of origin. Applies only if enforcedNamespaceLabel set to true. - items: - description: ObjectReference references a PodMonitor, ServiceMonitor, - Probe or PrometheusRule object. - properties: - group: - default: monitoring.coreos.com - description: Group of the referent. When not specified, it defaults - to `monitoring.coreos.com` - enum: - - monitoring.coreos.com - type: string - name: - description: Name of the referent. When not set, all resources - are matched. - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - minLength: 1 - type: string - resource: - description: Resource of the referent. - enum: - - prometheusrules - - servicemonitors - - podmonitors - - probes - type: string - required: - - namespace - - resource - type: object - type: array - externalLabels: - additionalProperties: - type: string - description: The labels to add to any time series or alerts when communicating - with external systems (federation, remote storage, Alertmanager). - type: object - externalUrl: - description: The external URL the Prometheus instances will be available - under. This is necessary to generate correct URLs. This is necessary - if Prometheus is not served from root of a DNS name. - type: string - hostAliases: - description: Pods' hostAliases configuration - items: - description: HostAlias holds the mapping between IP and hostnames - that will be injected as an entry in the pod's hosts file. - properties: - hostnames: - description: Hostnames for the above IP address. - items: - type: string - type: array - ip: - description: IP address of the host file entry. - type: string - required: - - hostnames - - ip - type: object - type: array - x-kubernetes-list-map-keys: - - ip - x-kubernetes-list-type: map - hostNetwork: - description: Use the host's network namespace if true. Make sure to - understand the security implications if you want to enable it. When - hostNetwork is enabled, this will set dnsPolicy to ClusterFirstWithHostNet - automatically. - type: boolean - ignoreNamespaceSelectors: - description: IgnoreNamespaceSelectors if set to true will ignore NamespaceSelector - settings from all PodMonitor, ServiceMonitor and Probe objects. - They will only discover endpoints within the namespace of the PodMonitor, - ServiceMonitor and Probe objects. Defaults to false. - type: boolean - image: - description: Image if specified has precedence over baseImage, tag - and sha combinations. Specifying the version is still necessary - to ensure the Prometheus Operator knows what version of Prometheus - is being configured. - type: string - imagePullPolicy: - description: Image pull policy for the 'prometheus', 'init-config-reloader' - and 'config-reloader' containers. See https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy - for more details. - enum: - - "" - - Always - - Never - - IfNotPresent - type: string - imagePullSecrets: - description: An optional list of references to secrets in the same - namespace to use for pulling prometheus and alertmanager images - from registries see http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod - items: - description: LocalObjectReference contains enough information to - let you locate the referenced object inside the same namespace. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - type: array - initContainers: - description: 'InitContainers allows adding initContainers to the pod - definition. Those can be used to e.g. fetch secrets for injection - into the Prometheus configuration from external sources. Any errors - during the execution of an initContainer will lead to a restart - of the Pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ - InitContainers described here modify an operator generated init - containers if they share the same name and modifications are done - via a strategic merge patch. The current init container name is: - `init-config-reloader`. Overriding init containers is entirely outside - the scope of what the maintainers will support and by doing so, - you accept that this behaviour may break at any time without notice.' - items: - description: A single application container that you want to run - within a pod. - properties: - args: - description: 'Arguments to the entrypoint. The container image''s - CMD is used if this is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. If a variable - cannot be resolved, the reference in the input string will - be unchanged. Double $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will - produce the string literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed within a shell. - The container image''s ENTRYPOINT is used if this is not provided. - Variable references $(VAR_NAME) are expanded using the container''s - environment. If a variable cannot be resolved, the reference - in the input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) syntax: - i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless of whether - the variable exists or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables to set in the container. - Cannot be updated. - items: - description: EnvVar represents an environment variable present - in a Container. - properties: - name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in - the container and any service environment variables. - If a variable cannot be resolved, the reference in the - input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) - syntax: i.e. "$$(VAR_NAME)" will produce the string - literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists - or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. - Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or - its key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: 'Selects a field of the pod: supports - metadata.name, metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the - specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, requests.cpu, - requests.memory and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the - exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's - namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment variables - in the container. The keys defined within a source must be - a C_IDENTIFIER. All invalid keys will be reported as an event - when the container is starting. When a key exists in multiple - sources, the value associated with the last source will take - precedence. Values defined by an Env with a duplicate key - will take precedence. Cannot be updated. - items: - description: EnvFromSource represents the source of a set - of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap must be - defined - type: boolean - type: object - x-kubernetes-map-type: atomic - prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the Secret must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - type: object - type: array - image: - description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level config management - to default or override container images in workload controllers - like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, Never, IfNotPresent. - Defaults to Always if :latest tag is specified, or IfNotPresent - otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management system should take - in response to container lifecycle events. Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately after a container - is created. If the handler fails, the container is terminated - and restarted according to its restart policy. Other management - of the container blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for - the command is root ('/') in the container's - filesystem. The command is simply exec'd, it is - not run inside a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you need - to explicitly call out to that shell. Exit status - of 0 is treated as live/healthy and non-zero is - unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to - the pod IP. You probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the request. - HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the - host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward compatibility. - There are no validation of this field and lifecycle - hooks will fail in runtime when tcp handler is specified. - properties: - host: - description: 'Optional: Host name to connect to, - defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately before a container - is terminated due to an API request or management event - such as liveness/startup probe failure, preemption, resource - contention, etc. The handler is not called if the container - crashes or exits. The Pod''s termination grace period - countdown begins before the PreStop hook is executed. - Regardless of the outcome of the handler, the container - will eventually terminate within the Pod''s termination - grace period (unless delayed by finalizers). Other management - of the container blocks until the hook completes or until - the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for - the command is root ('/') in the container's - filesystem. The command is simply exec'd, it is - not run inside a shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you need - to explicitly call out to that shell. Exit status - of 0 is treated as live/healthy and non-zero is - unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to - the pod IP. You probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the request. - HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the - host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: Deprecated. TCPSocket is NOT supported - as a LifecycleHandler and kept for the backward compatibility. - There are no validation of this field and lifecycle - hooks will fail in runtime when tcp handler is specified. - properties: - host: - description: 'Optional: Host name to connect to, - defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access - on the container. Number must be in the range - 1 to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container liveness. Container - will be restarted if the probe fails. Cannot be updated. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified as a DNS_LABEL. - Each container in a pod must have a unique name (DNS_LABEL). - Cannot be updated. - type: string - ports: - description: List of ports to expose from the container. Not - specifying a port here DOES NOT prevent that port from being - exposed. Any port which is listening on the default "0.0.0.0" - address inside a container will be accessible from the network. - Modifying this array with strategic merge patch may corrupt - the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. - Cannot be updated. - items: - description: ContainerPort represents a network port in a - single container. - properties: - containerPort: - description: Number of port to expose on the pod's IP - address. This must be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external port to. - type: string - hostPort: - description: Number of port to expose on the host. If - specified, this must be a valid port number, 0 < x < - 65536. If HostNetwork is specified, this must match - ContainerPort. Most containers do not need this. - format: int32 - type: integer - name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port in a pod - must have a unique name. Name for the port that can - be referred to by services. - type: string - protocol: - default: TCP - description: Protocol for port. Must be UDP, TCP, or SCTP. - Defaults to "TCP". - type: string - required: - - containerPort - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container service readiness. - Container will be removed from service endpoints if the probe - fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resizePolicy: - description: Resources resize policy for the container. - items: - description: ContainerResizePolicy represents resource resize - policy for the container. - properties: - resourceName: - description: 'Name of the resource to which this resource - resize policy applies. Supported values: cpu, memory.' - type: string - restartPolicy: - description: Restart policy to apply when specified resource - is resized. If not specified, it defaults to NotRequired. - type: string - required: - - resourceName - - restartPolicy - type: object - type: array - x-kubernetes-list-type: atomic - resources: - description: 'Compute Resources required by this container. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only - be set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where this - field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: 'SecurityContext defines the security options the - container should be run with. If set, the fields of SecurityContext - override the equivalent fields of PodSecurityContext. More - info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether - a process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by - the container runtime. Note that this field cannot be - set when spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes - in privileged containers are essentially equivalent to - root on the host. Defaults to false. Note that this field - cannot be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to - use for the containers. The default is DefaultProcMount - which uses the container runtime defaults for readonly - paths and masked paths. This requires the ProcMountType - feature flag to be enabled. Note that this field cannot - be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root - filesystem. Default is false. Note that this field cannot - be set when spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a - non-root user. If true, the Kubelet will validate the - image at runtime to ensure that it does not run as UID - 0 (root) and fail to start the container if it does. If - unset or false, no such validation will be performed. - May also be set in PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a - random SELinux context for each container. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. - Note that this field cannot be set when spec.os.name is - windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile - must be preconfigured on the node to work. Must be - a descending path, relative to the kubelet's configured - seccomp profile location. Must only be set if type - is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - - a profile defined in a file on the node should be - used. RuntimeDefault - the container runtime default - profile should be used. Unconfined - no profile should - be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the - GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components - that enable the WindowsHostProcessContainers feature - flag. Setting this field without the feature flag - will result in errors when validating the Pod. All - of a Pod's containers must have the same effective - HostProcess value (it is not allowed to have a mix - of HostProcess containers and non-HostProcess containers). In - addition, if HostProcess is true then HostNetwork - must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that the Pod has successfully - initialized. If specified, no other probes are executed until - this completes successfully. If this probe fails, the Pod - will be restarted, just as if the livenessProbe failed. This - can be used to provide different probe parameters at the beginning - of a Pod''s lifecycle, when it might take a long time to load - data or warm a cache, than during steady-state operation. - This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: Exec specifies the action to take. - properties: - command: - description: Command is the command line to execute - inside the container, the working directory for the - command is root ('/') in the container's filesystem. - The command is simply exec'd, it is not run inside - a shell, so traditional shell instructions ('|', etc) - won't work. To use a shell, you need to explicitly - call out to that shell. Exit status of 0 is treated - as live/healthy and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe - to be considered failed after having succeeded. Defaults - to 3. Minimum value is 1. - format: int32 - type: integer - grpc: - description: GRPC specifies an action involving a GRPC port. - properties: - port: - description: Port number of the gRPC service. Number - must be in the range 1 to 65535. - format: int32 - type: integer - service: - description: "Service is the name of the service to - place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - \n If this is not specified, the default behavior - is defined by gRPC." - type: string - required: - - port - type: object - httpGet: - description: HTTPGet specifies the http request to perform. - properties: - host: - description: Host name to connect to, defaults to the - pod IP. You probably want to set "Host" in httpHeaders - instead. - type: string - httpHeaders: - description: Custom headers to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes a custom header - to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting to the host. - Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container has - started before liveness probes are initiated. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - Default to 10 seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe - to be considered successful after having failed. Defaults - to 1. Must be 1 for liveness and startup. Minimum value - is 1. - format: int32 - type: integer - tcpSocket: - description: TCPSocket specifies an action involving a TCP - port. - properties: - host: - description: 'Optional: Host name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to access on - the container. Number must be in the range 1 to 65535. - Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully upon probe failure. The grace - period is the duration in seconds after the processes - running in the pod are sent a termination signal and the - time when the processes are forcibly halted with a kill - signal. Set this value longer than the expected cleanup - time for your process. If this value is nil, the pod's - terminationGracePeriodSeconds will be used. Otherwise, - this value overrides the value provided by the pod spec. - Value must be non-negative integer. The value zero indicates - stop immediately via the kill signal (no opportunity to - shut down). This is a beta field and requires enabling - ProbeTerminationGracePeriod feature gate. Minimum value - is 1. spec.terminationGracePeriodSeconds is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after which the probe times - out. Defaults to 1 second. Minimum value is 1. More info: - https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should allocate a buffer - for stdin in the container runtime. If this is not set, reads - from stdin in the container will always result in EOF. Default - is false. - type: boolean - stdinOnce: - description: Whether the container runtime should close the - stdin channel after it has been opened by a single attach. - When stdin is true the stdin stream will remain open across - multiple attach sessions. If stdinOnce is set to true, stdin - is opened on container start, is empty until the first client - attaches to stdin, and then remains open and accepts data - until the client disconnects, at which time stdin is closed - and remains closed until the container is restarted. If this - flag is false, a container processes that reads from stdin - will never receive an EOF. Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the file to which the - container''s termination message will be written is mounted - into the container''s filesystem. Message written is intended - to be brief final status, such as an assertion failure message. - Will be truncated by the node if greater than 4096 bytes. - The total message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination message should be - populated. File will use the contents of terminationMessagePath - to populate the container status message on both success and - failure. FallbackToLogsOnError will use the last chunk of - container log output if the termination message file is empty - and the container exited with an error. The log output is - limited to 2048 bytes or 80 lines, whichever is smaller. Defaults - to File. Cannot be updated. - type: string - tty: - description: Whether this container should allocate a TTY for - itself, also requires 'stdin' to be true. Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of block devices to be - used by the container. - items: - description: volumeDevice describes a mapping of a raw block - device within a container. - properties: - devicePath: - description: devicePath is the path inside of the container - that the device will be mapped to. - type: string - name: - description: name must match the name of a persistentVolumeClaim - in the pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the container's filesystem. - Cannot be updated. - items: - description: VolumeMount describes a mounting of a Volume - within a container. - properties: - mountPath: - description: Path within the container at which the volume - should be mounted. Must not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines how mounts are - propagated from the host to container and the other - way around. When not set, MountPropagationNone is used. - This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write otherwise - (false or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume from which the container's - volume should be mounted. Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves similarly - to SubPath but environment variable references $(VAR_NAME) - are expanded using the container's environment. Defaults - to "" (volume's root). SubPathExpr and SubPath are mutually - exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. If not specified, - the container runtime's default will be used, which might - be configured in the container image. Cannot be updated. - type: string - required: - - name - type: object - type: array - listenLocal: - description: ListenLocal makes the Prometheus server listen on loopback, - so that it does not bind against the Pod IP. - type: boolean - logFormat: - description: Log format for Prometheus to be configured with. - enum: - - "" - - logfmt - - json - type: string - logLevel: - description: Log level for Prometheus to be configured with. - enum: - - "" - - debug - - info - - warn - - error - type: string - minReadySeconds: - description: Minimum number of seconds for which a newly created pod - should be ready without any of its container crashing for it to - be considered available. Defaults to 0 (pod will be considered available - as soon as it is ready) This is an alpha field from kubernetes 1.22 - until 1.24 which requires enabling the StatefulSetMinReadySeconds - feature gate. - format: int32 - type: integer - nodeSelector: - additionalProperties: - type: string - description: Define which Nodes the Pods are scheduled on. - type: object - overrideHonorLabels: - description: When true, Prometheus resolves label conflicts by renaming - the labels in the scraped data to "exported_