From eba7e103f9486d62520059f94f09bdeb139d0d90 Mon Sep 17 00:00:00 2001 From: Fortune-Ndlovu Date: Tue, 22 Sep 2026 12:56:47 +0100 Subject: [PATCH 1/7] test(orchestrator-infra): add chart-testing CI for olmVersion v1 (#RHIDP-17168) Chart-testing only exercised the OLM v0 path; add a v1 CI values file, OLM v1 API CRD fixtures, and workflow hooks so both install paths are validated. --- .github/actions/test-charts/action.yml | 28 + .github/fixtures/olm-v1-api-crds.yaml | 1172 +++++++++++++++++ .../ci/upstream-olm-v1-values.yaml | 18 + 3 files changed, 1218 insertions(+) create mode 100644 .github/fixtures/olm-v1-api-crds.yaml create mode 100644 charts/orchestrator-infra/ci/upstream-olm-v1-values.yaml diff --git a/.github/actions/test-charts/action.yml b/.github/actions/test-charts/action.yml index 2c3bb37c..7507f26b 100644 --- a/.github/actions/test-charts/action.yml +++ b/.github/actions/test-charts/action.yml @@ -64,9 +64,14 @@ runs: echo "orchestratorCrdsNeeded=true" >> "$GITHUB_OUTPUT" echo "externalDbNeeded=true" >> "$GITHUB_OUTPUT" fi + if [[ "$INPUT_CHART" == "charts/orchestrator-infra" ]]; then + echo "orchestratorCrdsNeeded=true" >> "$GITHUB_OUTPUT" + echo "orchestratorOlmV1CrdsNeeded=true" >> "$GITHUB_OUTPUT" + fi elif [[ "$INPUT_ALL_CHARTS" == "true" ]]; then echo "changed=true" >> "$GITHUB_OUTPUT" echo "orchestratorCrdsNeeded=true" >> "$GITHUB_OUTPUT" + echo "orchestratorOlmV1CrdsNeeded=true" >> "$GITHUB_OUTPUT" echo "externalDbNeeded=true" >> "$GITHUB_OUTPUT" else listChanged=$(ct list-changed --target-branch "$INPUT_TARGET_BRANCH") @@ -76,6 +81,10 @@ runs: echo "orchestratorCrdsNeeded=true" >> "$GITHUB_OUTPUT" echo "externalDbNeeded=true" >> "$GITHUB_OUTPUT" fi + if grep -q 'charts/orchestrator-infra' <<< "$listChanged"; then + echo "orchestratorCrdsNeeded=true" >> "$GITHUB_OUTPUT" + echo "orchestratorOlmV1CrdsNeeded=true" >> "$GITHUB_OUTPUT" + fi fi fi @@ -112,6 +121,19 @@ runs: --values "$VALUES_FILE" 2>&1) || { echo "$output"; exit 1; } done + - name: Helm template sanity check (orchestrator-infra ci values) + if: steps.list-changed.outputs.changed == 'true' && inputs.chart == 'charts/orchestrator-infra' + shell: bash + env: + INPUT_CHART: ${{ inputs.chart }} + run: | + for ci_values in "$INPUT_CHART"/ci/*-values.yaml; do + [[ -f "$ci_values" ]] || continue + echo "==> helm template: $(basename "$ci_values")" + output=$(helm template test-release "$INPUT_CHART" --values "$ci_values" 2>&1) || { echo "$output"; exit 1; } + echo "$output" | grep -E '^kind:' | sort | uniq -c + done + - name: Generate KinD Config if: steps.list-changed.outputs.changed == 'true' shell: bash @@ -194,6 +216,12 @@ runs: kubectl create -f charts/orchestrator-infra/crds/knative-serving/knative-serving-crd.yaml kubectl create -f "https://github.com/apache/incubator-kie-tools/releases/download/${SONATAFLOW_OPERATOR_VERSION}/apache-kie-${SONATAFLOW_OPERATOR_VERSION}-incubating-sonataflow-operator.yaml" + - name: Install OLM v1 API CRDs (orchestrator-infra chart-testing) + if: steps.list-changed.outputs.orchestratorOlmV1CrdsNeeded == 'true' + shell: bash + run: | + kubectl create -f .github/fixtures/olm-v1-api-crds.yaml + - name: Set up external services and test resources for rhdh if: steps.list-changed.outputs.externalDbNeeded == 'true' shell: bash diff --git a/.github/fixtures/olm-v1-api-crds.yaml b/.github/fixtures/olm-v1-api-crds.yaml new file mode 100644 index 00000000..384a3afd --- /dev/null +++ b/.github/fixtures/olm-v1-api-crds.yaml @@ -0,0 +1,1172 @@ +# OLM v1 API CRDs for orchestrator-infra chart-testing (olmVersion=v1). +# Source: operator-framework/operator-controller v1.11.0 (CRDs only; controller not installed). +# https://github.com/operator-framework/operator-controller/releases/tag/v1.11.0 +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + olm.operatorframework.io/generator: standard + name: clustercatalogs.olm.operatorframework.io +spec: + group: olm.operatorframework.io + names: + kind: ClusterCatalog + listKind: ClusterCatalogList + plural: clustercatalogs + singular: clustercatalog + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.lastUnpacked + name: LastUnpacked + type: date + - jsonPath: .status.conditions[?(@.type=="Serving")].status + name: Serving + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: 'ClusterCatalog makes File-Based Catalog (FBC) data available + to your cluster. + + For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs' + 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: 'spec is a required field that defines the desired state + of the ClusterCatalog. + + The controller ensures that the catalog is unpacked and served over + the catalog content HTTP server.' + properties: + availabilityMode: + default: Available + description: 'availabilityMode is an optional field that defines how + the ClusterCatalog is made available to clients on the cluster. + + + Allowed values are "Available", "Unavailable", or omitted. + + + When omitted, the default value is "Available". + + + When set to "Available", the catalog contents are unpacked and served + over the catalog content HTTP server. + + Clients should consider this ClusterCatalog and its contents as + usable. + + + When set to "Unavailable", the catalog contents are no longer served + over the catalog content HTTP server. + + Treat this the same as if the ClusterCatalog does not exist. + + Use "Unavailable" when you want to keep the ClusterCatalog but treat + it as if it doesn''t exist.' + enum: + - Unavailable + - Available + type: string + priority: + default: 0 + description: 'priority is an optional field that defines a priority + for this ClusterCatalog. + + + Clients use the ClusterCatalog priority as a tie-breaker between + ClusterCatalogs that meet their requirements. + + Higher numbers mean higher priority. + + + Clients decide how to handle scenarios where multiple ClusterCatalogs + with the same priority meet their requirements. + + Clients should prompt users for additional input to break the tie. + + + When omitted, the default priority is 0. + + + Use negative numbers to specify a priority lower than the default. + + Use positive numbers to specify a priority higher than the default. + + + The lowest possible value is -2147483648. + + The highest possible value is 2147483647.' + format: int32 + maximum: 2147483647 + minimum: -2147483648 + type: integer + source: + description: "source is a required field that defines the source of\ + \ a catalog.\nA catalog contains information on content that can\ + \ be installed on a cluster.\nThe catalog source makes catalog contents\ + \ discoverable and usable by other on-cluster components.\nThese\ + \ components can present the content in a GUI dashboard or install\ + \ content from the catalog on the cluster.\nThe catalog source must\ + \ contain catalog metadata in the File-Based Catalog (FBC) format.\n\ + For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs.\n\ + \nBelow is a minimal example of a ClusterCatalogSpec that sources\ + \ a catalog from an image:\n\n source:\n type: Image\n image:\n\ + \ ref: quay.io/operatorhubio/catalog:latest" + properties: + image: + description: 'image configures how catalog contents are sourced + from an OCI image. + + It is required when type is Image, and forbidden otherwise.' + properties: + pollIntervalMinutes: + description: 'pollIntervalMinutes is an optional field that + sets the interval, in minutes, at which the image source + is polled for new content. + + You cannot specify pollIntervalMinutes when ref is a digest-based + reference. + + + When omitted, the image is not polled for new content.' + minimum: 1 + type: integer + ref: + description: 'ref is a required field that defines the reference + to a container image containing catalog contents. + + It cannot be more than 1000 characters. + + + A reference has 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 + type: string + x-kubernetes-validations: + - message: must start with a valid domain. valid domains must + be alphanumeric characters (lowercase and uppercase) separated + by the "." character. + rule: self.matches('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])((\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(:[0-9]+)?\\b') + - message: a valid name is required. valid names must contain + lowercase alphanumeric characters separated only by the + ".", "_", "__", "-" characters. + rule: self.find('(\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)') + != "" + - message: must end with a digest or a tag + rule: self.find('(@.*:)') != "" || self.find(':.*$') != + "" + - message: tag is invalid. the tag must not be more than 127 + characters + rule: 'self.find(''(@.*:)'') == "" ? (self.find('':.*$'') + != "" ? self.find('':.*$'').substring(1).size() <= 127 + : true) : true' + - message: tag is invalid. valid tags must begin with a word + character (alphanumeric + "_") followed by word characters + or ".", and "-" characters + rule: 'self.find(''(@.*:)'') == "" ? (self.find('':.*$'') + != "" ? self.find('':.*$'').matches('':[\\w][\\w.-]*$'') + : true) : true' + - message: digest algorithm is not valid. valid algorithms + must start with an uppercase or lowercase alpha character + followed by alphanumeric characters and may contain the + "-", "_", "+", and "." characters. + rule: 'self.find(''(@.*:)'') != "" ? self.find(''(@.*:)'').matches(''(@[A-Za-z][A-Za-z0-9]*([-_+.][A-Za-z][A-Za-z0-9]*)*[:])'') + : true' + - message: digest is not valid. the encoded string must be + at least 32 characters + rule: 'self.find(''(@.*:)'') != "" ? self.find('':.*$'').substring(1).size() + >= 32 : true' + - message: digest is not valid. the encoded string must only + contain hex characters (A-F, a-f, 0-9) + rule: 'self.find(''(@.*:)'') != "" ? self.find('':.*$'').matches('':[0-9A-Fa-f]*$'') + : true' + required: + - ref + type: object + x-kubernetes-validations: + - message: cannot specify pollIntervalMinutes while using digest-based + image + rule: 'self.ref.find(''(@.*:)'') != "" ? !has(self.pollIntervalMinutes) + : true' + type: + description: 'type is a required field that specifies the type + of source for the catalog. + + + The only allowed value is "Image". + + + When set to "Image", the ClusterCatalog content is 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 + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: image is required when source type is Image, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''Image'' ? has(self.image) + : !has(self.image)' + required: + - source + type: object + status: + description: "status contains the following information about the state\ + \ of the ClusterCatalog:\n - Whether the catalog contents are being\ + \ served via the catalog content HTTP server\n - Whether the ClusterCatalog\ + \ is progressing to a new state\n - A reference to the source from\ + \ which the catalog contents were retrieved" + properties: + conditions: + description: "conditions represents the current state of this ClusterCatalog.\n\ + \nThe current condition types are Serving and Progressing.\n\nThe\ + \ Serving condition represents whether the catalog contents are\ + \ being served via the HTTP(S) web server:\n - When status is True\ + \ and reason is Available, the catalog contents are being served.\n\ + \ - When status is False and reason is Unavailable, the catalog\ + \ contents are not being served because the contents are not yet\ + \ available.\n - When status is False and reason is UserSpecifiedUnavailable,\ + \ the catalog contents are not being served because the catalog\ + \ has been intentionally marked as unavailable.\n\nThe Progressing\ + \ condition represents whether the ClusterCatalog is progressing\ + \ or is ready to progress towards a new state:\n - When status\ + \ is True and reason is Retrying, an error occurred that may be\ + \ resolved on subsequent reconciliation attempts.\n - When status\ + \ is True and reason is Succeeded, the ClusterCatalog has successfully\ + \ progressed to a new state and is ready to continue progressing.\n\ + \ - When status is False and reason is Blocked, an error occurred\ + \ that requires manual intervention for recovery.\n\nIf the system\ + \ initially fetched contents and polling identifies updates, both\ + \ conditions can be active simultaneously:\n - The Serving condition\ + \ remains True with reason Available because the previous contents\ + \ are still served via the HTTP(S) web server.\n - The Progressing\ + \ condition is True with reason Retrying because the system is working\ + \ to serve the new version." + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: 'lastTransitionTime is the last time the condition + transitioned from one status to another. + + This should be when the underlying condition changed. If + that is not known, then using the time when the API field + changed is acceptable.' + format: date-time + type: string + message: + description: 'message is a human readable message indicating + details about the transition. + + This may be an empty string.' + maxLength: 32768 + type: string + observedGeneration: + description: 'observedGeneration represents the .metadata.generation + that the condition was set based upon. + + For instance, if .metadata.generation is currently 12, but + the .status.conditions[x].observedGeneration is 9, the condition + is out of date + + with respect to the current state of the instance.' + format: int64 + minimum: 0 + type: integer + reason: + description: 'reason contains a programmatic identifier indicating + the reason for the condition''s last transition. + + Producers of specific condition types may define expected + values and meanings for this field, + + and whether the values are considered a guaranteed API. + + The value should be a CamelCase string. + + This field may not be empty.' + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - 'True' + - 'False' + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lastUnpacked: + description: 'lastUnpacked represents the last time the catalog contents + were extracted from their source format. + + For example, when using an Image source, the OCI image is pulled + and image layers are written to a file-system backed cache. + + This extraction from the source format is called "unpacking".' + format: date-time + type: string + resolvedSource: + description: resolvedSource contains information about the resolved + source based on the source type. + properties: + image: + description: 'image contains resolution information for a catalog + sourced from an image. + + It must be set when type is Image, and forbidden otherwise.' + properties: + ref: + description: 'ref contains the resolved image digest-based + reference. + + The digest format allows you to use other tooling to fetch + the exact OCI manifests + + that were used to extract the catalog contents.' + maxLength: 1000 + type: string + x-kubernetes-validations: + - message: must start with a valid domain. valid domains must + be alphanumeric characters (lowercase and uppercase) separated + by the "." character. + rule: self.matches('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])((\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(:[0-9]+)?\\b') + - message: a valid name is required. valid names must contain + lowercase alphanumeric characters separated only by the + ".", "_", "__", "-" characters. + rule: self.find('(\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)') + != "" + - message: must end with a digest + rule: self.find('(@.*:)') != "" + - message: digest algorithm is not valid. valid algorithms + must start with an uppercase or lowercase alpha character + followed by alphanumeric characters and may contain the + "-", "_", "+", and "." characters. + rule: 'self.find(''(@.*:)'') != "" ? self.find(''(@.*:)'').matches(''(@[A-Za-z][A-Za-z0-9]*([-_+.][A-Za-z][A-Za-z0-9]*)*[:])'') + : true' + - message: digest is not valid. the encoded string must be + at least 32 characters + rule: 'self.find(''(@.*:)'') != "" ? self.find('':.*$'').substring(1).size() + >= 32 : true' + - message: digest is not valid. the encoded string must only + contain hex characters (A-F, a-f, 0-9) + rule: 'self.find(''(@.*:)'') != "" ? self.find('':.*$'').matches('':[0-9A-Fa-f]*$'') + : true' + required: + - ref + type: object + type: + description: 'type is a required field that specifies the type + of source for the catalog. + + + The only allowed value is "Image". + + + When set to "Image", information about the resolved image source + is set in the image field.' + enum: + - Image + type: string + required: + - image + - type + type: object + x-kubernetes-validations: + - message: image is required when source type is Image, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''Image'' ? has(self.image) + : !has(self.image)' + urls: + description: urls contains the URLs that can be used to access the + catalog. + properties: + base: + description: "base is a cluster-internal URL that provides endpoints\ + \ for accessing the catalog content.\n\nClients should append\ + \ the path for the endpoint they want to access.\n\nCurrently,\ + \ only a single endpoint is served and is accessible at the\ + \ path /api/v1.\n\nThe endpoints served for the v1 API are:\n\ + \ - /all - this endpoint returns the entire catalog contents\ + \ in the FBC format\n\nNew endpoints may be added as needs evolve." + maxLength: 525 + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + - message: scheme must be either http or https + rule: 'isURL(self) ? (url(self).getScheme() == "http" || url(self).getScheme() + == "https") : true' + required: + - base + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.21.0 + olm.operatorframework.io/generator: standard + name: clusterextensions.olm.operatorframework.io +spec: + group: olm.operatorframework.io + names: + kind: ClusterExtension + listKind: ClusterExtensionList + plural: clusterextensions + singular: clusterextension + scope: Cluster + versions: + - 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: v1 + schema: + openAPIV3Schema: + description: ClusterExtension is the Schema for the clusterextensions API + 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: spec is an optional field that defines the desired state + of the ClusterExtension. + properties: + install: + description: 'install is optional and configures installation options + for the ClusterExtension, + + such as the pre-flight check configuration.' + properties: + preflight: + description: 'preflight is optional and configures the checks + that 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 is used.' + properties: + crdUpgradeSafety: + description: 'crdUpgradeSafety configures the CRD Upgrade + Safety pre-flight checks that run + + before upgrades of installed content. + + + The CRD Upgrade Safety pre-flight check safeguards from + unintended consequences of upgrading a CRD, + + such as data loss.' + properties: + enforcement: + description: 'enforcement is required and configures 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 is skipped during an upgrade operation. + + Use this option with caution as unintended consequences + such as data loss can occur. + + + When set to "Strict", the CRD Upgrade Safety pre-flight + check runs during an upgrade operation.' + enum: + - None + - Strict + type: string + required: + - enforcement + type: object + required: + - crdUpgradeSafety + type: object + x-kubernetes-validations: + - message: at least one of [crdUpgradeSafety] are required when + preflight is specified + rule: has(self.crdUpgradeSafety) + type: object + x-kubernetes-validations: + - message: at least one of [preflight] are required when install is + specified + rule: has(self.preflight) + namespace: + description: 'namespace specifies a Kubernetes namespace. + + It 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. + + + The namespace field 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 deprecated field and is completely + ignored. + + OLMv1 is a single-tenant system where users with ClusterExtension + write access are + + effectively delegated cluster-admin trust. The operator-controller + runs with + + cluster-admin privileges and uses its own service account for all + cluster interactions. + + + Deprecated: serviceAccount is no longer used and will be removed + in a future release.' + minProperties: 1 + properties: + name: + description: "name is a deprecated field and is completely ignored.\n\ + \nDeprecated: name is no longer used and will be removed in\ + \ a future release.\n\nThe name field follows the DNS subdomain\ + \ standard as defined in [RFC 1123].\nIt must contain only lowercase\ + \ alphanumeric characters, hyphens (-) or periods (.),\nstart\ + \ and end with an alphanumeric character, and be no longer than\ + \ 253 characters.\n\nSome examples of valid values are:\n -\ + \ some-serviceaccount\n - 123-serviceaccount\n - 1-serviceaccount-2\n\ + \ - someserviceaccount\n - some.serviceaccount\n\nSome examples\ + \ of invalid values are:\n - -some-serviceaccount\n - some-serviceaccount-\n\ + \n[RFC 1123]: https://tools.ietf.org/html/rfc1123" + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name is immutable once set but may be cleared + 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])?)*$") + type: object + source: + description: "source is required and selects the installation source\ + \ of content for this ClusterExtension.\nSet the sourceType field\ + \ to perform the selection.\n\nCatalog is currently the only implemented\ + \ sourceType.\nSetting sourceType to \"Catalog\" requires the catalog\ + \ field to also be defined.\n\nBelow is a minimal example of a source\ + \ definition (in yaml):\n\nsource:\n sourceType: Catalog\n catalog:\n\ + \ packageName: example-package" + properties: + catalog: + description: 'catalog configures how information is sourced from + a catalog. + + It is required when sourceType is "Catalog", and forbidden otherwise.' + properties: + channels: + description: "channels is optional and specifies a set of\ + \ channels belonging to the package\nspecified in the packageName\ + \ field.\n\nA channel is a package-author-defined stream\ + \ of updates for an extension.\n\nEach channel in the list\ + \ must follow the DNS subdomain standard as defined in [RFC\ + \ 1123].\nIt must contain only lowercase alphanumeric characters,\ + \ hyphens (-) or periods (.),\nstart and end with an alphanumeric\ + \ character, and be no longer than 253 characters.\nYou\ + \ can specify no more than 256 channels.\n\nWhen specified,\ + \ it constrains the set of installable bundles and the automated\ + \ upgrade path.\nThis constraint is an AND operation with\ + \ the version field. For example:\n - Given channel is\ + \ set to \"foo\"\n - Given version is set to \">=1.0.0,\ + \ <1.5.0\"\n - Only bundles that exist in channel \"foo\"\ + \ AND satisfy the version range comparison are considered\ + \ installable\n - Automatic upgrades are constrained to\ + \ upgrade edges defined by the selected channel\n\nWhen\ + \ unspecified, upgrade edges across all channels are used\ + \ to identify valid automatic upgrade paths.\n\nSome examples\ + \ of valid values are:\n - 1.1.x\n - alpha\n - stable\n\ + \ - stable-v1\n - v1-stable\n - dev-preview\n - preview\n\ + \ - community\n\nSome examples of invalid values are:\n\ + \ - -some-channel\n - some-channel-\n - thisisareallylongchannelnamethatisgreaterthanthemaximumlength\n\ + \ - original_40\n - --default-channel\n\n[RFC 1123]: https://tools.ietf.org/html/rfc1123" + items: + maxLength: 253 + 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 specifies the name of the package\ + \ to be installed and is used to filter\nthe content from\ + \ catalogs.\n\nIt is required, immutable, and follows the\ + \ DNS subdomain standard as defined in [RFC 1123].\nIt must\ + \ contain only lowercase alphanumeric characters, hyphens\ + \ (-) or periods (.),\nstart and end with an alphanumeric\ + \ character, and be no longer than 253 characters.\n\nSome\ + \ examples of valid values are:\n - some-package\n - 123-package\n\ + \ - 1-package-2\n - somepackage\n\nSome examples of invalid\ + \ values are:\n - -some-package\n - some-package-\n -\ + \ thisisareallylongpackagenamethatisgreaterthanthemaximumlength\n\ + \ - some.package\n\n[RFC 1123]: https://tools.ietf.org/html/rfc1123" + maxLength: 253 + 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 optional and filters the set of + ClusterCatalogs used in the bundle selection process. + + + When unspecified, all ClusterCatalogs are used in the bundle + selection process.' + 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 + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + 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 + upgradeConstraintPolicy: + default: CatalogProvided + description: 'upgradeConstraintPolicy is optional and controls + whether the upgrade paths defined in the catalog + + are enforced for the package referenced in the packageName + field. + + + Allowed values are "CatalogProvided", "SelfCertified", or + omitted. + + + When set to "CatalogProvided", automatic upgrades only occur + when upgrade constraints specified by the package + + author are met. + + + When set to "SelfCertified", the upgrade constraints specified + by the package author are ignored. + + This allows 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. + + Use this option only if you have independently verified + the changes. + + + When omitted, the default value is "CatalogProvided".' + enum: + - CatalogProvided + - SelfCertified + type: string + version: + description: "version is an optional semver constraint (a\ + \ specific version or range of versions).\nWhen unspecified,\ + \ the latest version available is installed.\n\nAcceptable\ + \ version ranges are no longer than 64 characters.\nVersion\ + \ ranges are composed of comma- or space-delimited values\ + \ and one or more comparison operators,\nknown as comparison\ + \ strings.\nYou can add additional comparison strings using\ + \ the OR operator (||).\n\n# Range Comparisons\n\nTo specify\ + \ a version range, you can use a comparison string like\ + \ \">=3.0,\n<3.6\". When specifying a range, automatic updates\ + \ will occur within that\nrange. The example comparison\ + \ string means \"install any version greater than\nor equal\ + \ to 3.0.0 but less than 3.6.0.\". It also states intent\ + \ that if any\nupgrades are available within the version\ + \ range after initial installation,\nthose upgrades should\ + \ be automatically performed.\n\n# Pinned Versions\n\nTo\ + \ specify an exact version to install you can use a version\ + \ range that\n\"pins\" to a specific version. When pinning\ + \ to a specific version, no\nautomatic updates will occur.\ + \ An example of a pinned version range is\n\"0.6.0\", which\ + \ means \"only install version 0.6.0 and never\nupgrade\ + \ from this version\".\n\n# Basic Comparison Operators\n\ + \nThe basic comparison operators and their meanings are:\n\ + \ - \"=\", equal (not aliased to an operator)\n - \"!=\"\ + , not equal\n - \"<\", less than\n - \">\", greater than\n\ + \ - \">=\", greater than OR equal to\n - \"<=\", less\ + \ than OR equal to\n\n# Wildcard Comparisons\n\nYou can\ + \ use the \"x\", \"X\", and \"*\" characters as wildcard\ + \ characters in all\ncomparison operations. Some examples\ + \ of using the wildcard characters:\n - \"1.2.x\", \"1.2.X\"\ + , and \"1.2.*\" is equivalent to \">=1.2.0, < 1.3.0\"\n\ + \ - \">= 1.2.x\", \">= 1.2.X\", and \">= 1.2.*\" is equivalent\ + \ to \">= 1.2.0\"\n - \"<= 2.x\", \"<= 2.X\", and \"<=\ + \ 2.*\" is equivalent to \"< 3\"\n - \"x\", \"X\", and\ + \ \"*\" is equivalent to \">= 0.0.0\"\n\n# Patch Release\ + \ Comparisons\n\nWhen you want to specify a minor version\ + \ up to the next major version you\ncan use the \"~\" character\ + \ to perform patch comparisons. Some examples:\n - \"~1.2.3\"\ + \ is equivalent to \">=1.2.3, <1.3.0\"\n - \"~1\" and \"\ + ~1.x\" is equivalent to \">=1, <2\"\n - \"~2.3\" is equivalent\ + \ to \">=2.3, <2.4\"\n - \"~1.2.x\" is equivalent to \"\ + >=1.2.0, <1.3.0\"\n\n# Major Release Comparisons\n\nYou\ + \ can use the \"^\" character to make major release comparisons\ + \ after a\nstable 1.0.0 version is published. If there is\ + \ no stable version published, // minor versions define\ + \ the stability level. Some examples:\n - \"^1.2.3\" is\ + \ equivalent to \">=1.2.3, <2.0.0\"\n - \"^1.2.x\" is equivalent\ + \ to \">=1.2.0, <2.0.0\"\n - \"^2.3\" is equivalent to\ + \ \">=2.3, <3\"\n - \"^2.x\" is equivalent to \">=2.0.0,\ + \ <3\"\n - \"^0.2.3\" is equivalent to \">=0.2.3, <0.3.0\"\ + \n - \"^0.2\" is equivalent to \">=0.2.0, <0.3.0\"\n -\ + \ \"^0.0.3\" is equvalent to \">=0.0.3, <0.0.4\"\n - \"\ + ^0.0\" is equivalent to \">=0.0.0, <0.1.0\"\n - \"^0\"\ + \ is equivalent to \">=0.0.0, <1.0.0\"\n\n# OR Comparisons\n\ + You can use the \"||\" character to represent an OR operation\ + \ in the version\nrange. Some examples:\n - \">=1.2.3,\ + \ <2.0.0 || >3.0.0\"\n - \"^0 || ^3 || ^5\"\n\nFor more\ + \ information on semver, please see https://semver.org/" + maxLength: 64 + 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 + sourceType: + description: 'sourceType is required and specifies the type of + install source. + + + The only allowed value is "Catalog". + + + When set to "Catalog", information for determining the appropriate + bundle of content to install + + is fetched from ClusterCatalog resources on the cluster. + + When using the Catalog sourceType, the catalog field must also + be set.' + enum: + - Catalog + type: string + required: + - sourceType + type: object + x-kubernetes-validations: + - 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: + - namespace + - source + type: object + status: + description: status is an optional field that defines the observed state + of the ClusterExtension. + properties: + conditions: + description: "conditions represents the current state of the ClusterExtension.\n\ + \nThe set of condition types which apply to all spec.source variations\ + \ are Installed and Progressing.\n\nThe Installed condition represents\ + \ whether the bundle has been installed for this ClusterExtension:\n\ + \ - When Installed is True and the Reason is Succeeded, the bundle\ + \ has been successfully installed.\n - When Installed is False\ + \ and the Reason is Failed, the bundle has failed to install.\n\n\ + The Progressing condition represents whether or not the ClusterExtension\ + \ is advancing towards a new state.\nWhen Progressing is True and\ + \ the Reason is Succeeded, the ClusterExtension is making progress\ + \ towards a new state.\nWhen Progressing is True and the Reason\ + \ is Retrying, the ClusterExtension has encountered an error that\ + \ could be resolved on subsequent reconciliation attempts.\nWhen\ + \ Progressing is False and the Reason is Blocked, the ClusterExtension\ + \ has encountered an error that requires manual intervention for\ + \ recovery.\n\nWhen the ClusterExtension is sourced from a catalog,\ + \ it surfaces deprecation conditions based on catalog metadata.\n\ + These are indications from a package owner to guide users away from\ + \ a particular package, channel, or bundle:\n - BundleDeprecated\ + \ is True if the installed bundle is marked deprecated, False if\ + \ not deprecated, or Unknown if no bundle is installed yet or if\ + \ catalog data is unavailable.\n - ChannelDeprecated is True if\ + \ any requested channel is marked deprecated, False if not deprecated,\ + \ or Unknown if catalog data is unavailable.\n - PackageDeprecated\ + \ is True if the requested package is marked deprecated, False if\ + \ not deprecated, or Unknown if catalog data is unavailable.\n \ + \ - Deprecated is a rollup condition that is True when any deprecation\ + \ exists, False when none exist, or Unknown when catalog data is\ + \ unavailable." + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: 'lastTransitionTime is the last time the condition + transitioned from one status to another. + + This should be when the underlying condition changed. If + that is not known, then using the time when the API field + changed is acceptable.' + format: date-time + type: string + message: + description: 'message is a human readable message indicating + details about the transition. + + This may be an empty string.' + maxLength: 32768 + type: string + observedGeneration: + description: 'observedGeneration represents the .metadata.generation + that the condition was set based upon. + + For instance, if .metadata.generation is currently 12, but + the .status.conditions[x].observedGeneration is 9, the condition + is out of date + + with respect to the current state of the instance.' + format: int64 + minimum: 0 + type: integer + reason: + description: 'reason contains a programmatic identifier indicating + the reason for the condition''s last transition. + + Producers of specific condition types may define expected + values and meanings for this field, + + and whether the values are considered a guaranteed API. + + The value should be a CamelCase string. + + This field may not be empty.' + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - 'True' + - 'False' + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + install: + description: install is a representation of the current installation + status for this ClusterExtension. + properties: + bundle: + description: 'bundle is required and 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 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 required and references the version + that this bundle represents. + + It 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 + type: object + required: + - bundle + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/orchestrator-infra/ci/upstream-olm-v1-values.yaml b/charts/orchestrator-infra/ci/upstream-olm-v1-values.yaml new file mode 100644 index 00000000..df021633 --- /dev/null +++ b/charts/orchestrator-infra/ci/upstream-olm-v1-values.yaml @@ -0,0 +1,18 @@ +# Chart-testing values for the OLM v1 install path (ClusterExtension resources). +# Mirrors upstream-olm-values.yaml namespace overrides for KinD + upstream OLM. +olmVersion: v1 + +serverlessLogicOperator: + enabled: true + createNamespace: false + subscription: + namespace: operators + spec: + sourceNamespace: olm + +serverlessOperator: + createNamespace: false + subscription: + namespace: operators + spec: + sourceNamespace: olm From 9528bd2f6d4c8d6e51583eb8e53e7a613a50642c Mon Sep 17 00:00:00 2001 From: Fortune-Ndlovu Date: Tue, 22 Sep 2026 12:58:10 +0100 Subject: [PATCH 2/7] chore(orchestrator-infra): bump chart version to 0.7.1 (#RHIDP-17168) Patch bump required by chart-testing when the chart directory changes. --- charts/orchestrator-infra/Chart.yaml | 2 +- charts/orchestrator-infra/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/orchestrator-infra/Chart.yaml b/charts/orchestrator-infra/Chart.yaml index de9f53b2..522f6961 100644 --- a/charts/orchestrator-infra/Chart.yaml +++ b/charts/orchestrator-infra/Chart.yaml @@ -14,4 +14,4 @@ maintainers: type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.7.0 +version: 0.7.1 diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index f5c824bb..6f135426 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Infra Chart for OpenShift -![Version: 0.7.0](https://img.shields.io/badge/Version-0.7.0-informational?style=flat-square) +![Version: 0.7.1](https://img.shields.io/badge/Version-0.7.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Operator and OpenShift Serverless Logic Operator, both required to configure Red Hat Developer Hub to use the Orchestrator. @@ -25,7 +25,7 @@ Kubernetes: `>= 1.25.0-0` ```console helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-orchestrator-infra redhat-developer/redhat-developer-hub-orchestrator-infra --version 0.7.0 +helm install my-orchestrator-infra redhat-developer/redhat-developer-hub-orchestrator-infra --version 0.7.1 ``` > **Tip**: List all releases using `helm list` From 2ee1cffc354f805a32e35308ccc5b3df63f5e430 Mon Sep 17 00:00:00 2001 From: Fortune-Ndlovu Date: Thu, 24 Sep 2026 10:22:30 +0100 Subject: [PATCH 3/7] chore(orchestrator-infra): script OLM v1 CRD fixture maintenance (#RHIDP-17168) Document how to refresh the KinD CRD fixture and add a fetch script with --check so updates stay aligned with operator-controller releases. Signed-off-by: Fortune Ndlovu --- .github/fixtures/README.md | 19 +++++++ .github/fixtures/olm-v1-api-crds.yaml | 4 +- hack/fetch-olm-v1-api-crds.sh | 82 +++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 .github/fixtures/README.md create mode 100755 hack/fetch-olm-v1-api-crds.sh diff --git a/.github/fixtures/README.md b/.github/fixtures/README.md new file mode 100644 index 00000000..a31c2fd2 --- /dev/null +++ b/.github/fixtures/README.md @@ -0,0 +1,19 @@ +# CI fixtures + +## OLM v1 API CRDs (`olm-v1-api-crds.yaml`) + +KinD chart-testing for `charts/orchestrator-infra` with `olmVersion: v1` needs the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so `ClusterExtension` manifests can be admitted. The full `operator-controller` is not installed in CI—only these CRD definitions. + +**Update when bumping OLM v1 API version:** + +1. Set `OPERATOR_CONTROLLER_VERSION` if needed (default `v1.11.0` in the script). +2. From the repo root: `./hack/fetch-olm-v1-api-crds.sh` +3. Commit the updated `olm-v1-api-crds.yaml`. + +**Verify without writing:** + +```bash +./hack/fetch-olm-v1-api-crds.sh --check +``` + +The script downloads the upstream release manifest, keeps `CustomResourceDefinition` objects only, and records a `sha256(crds)` line in the file header. diff --git a/.github/fixtures/olm-v1-api-crds.yaml b/.github/fixtures/olm-v1-api-crds.yaml index 384a3afd..46748b61 100644 --- a/.github/fixtures/olm-v1-api-crds.yaml +++ b/.github/fixtures/olm-v1-api-crds.yaml @@ -1,6 +1,8 @@ # OLM v1 API CRDs for orchestrator-infra chart-testing (olmVersion=v1). # Source: operator-framework/operator-controller v1.11.0 (CRDs only; controller not installed). -# https://github.com/operator-framework/operator-controller/releases/tag/v1.11.0 +# Regenerate: ./hack/fetch-olm-v1-api-crds.sh +# Upstream: https://github.com/operator-framework/operator-controller/releases/tag/v1.11.0 +# sha256(crds): 79cf2241267b6ca9a35da5dd64beadd6d8a8f5c371405251a204392f3112924c apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/hack/fetch-olm-v1-api-crds.sh b/hack/fetch-olm-v1-api-crds.sh new file mode 100755 index 00000000..9cec250b --- /dev/null +++ b/hack/fetch-olm-v1-api-crds.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +set -euo pipefail + +OPERATOR_CONTROLLER_VERSION="${OPERATOR_CONTROLLER_VERSION:-v1.11.0}" +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" +OUTPUT_FILE="${REPO_ROOT}/.github/fixtures/olm-v1-api-crds.yaml" +UPSTREAM_URL="https://github.com/operator-framework/operator-controller/releases/download/${OPERATOR_CONTROLLER_VERSION}/operator-controller.yaml" + +usage() { + cat <&2 + exit 2 +fi + +if ! command -v yq >/dev/null 2>&1; then + echo "yq is required (see https://github.com/mikefarah/yq)" >&2 + exit 1 +fi + +tmpdir="$(mktemp -d)" +trap 'rm -rf "${tmpdir}"' EXIT + +curl -fsSL --proto '=https' "${UPSTREAM_URL}" -o "${tmpdir}/operator-controller.yaml" +yq ea 'select(.kind == "CustomResourceDefinition")' "${tmpdir}/operator-controller.yaml" \ + > "${tmpdir}/crds.yaml" + +canonical_crds() { + yq -o=json -I=0 ea 'select(.kind == "CustomResourceDefinition")' "$1" +} + +upstream_canonical="$(canonical_crds "${tmpdir}/operator-controller.yaml")" +sha256="$(printf '%s' "${upstream_canonical}" | sha256sum | awk '{print $1}')" + +write_fixture() { + local dest=$1 + { + cat < "${dest}" +} + +if [[ "${check_only}" == true ]]; then + if [[ ! -f "${OUTPUT_FILE}" ]]; then + echo "Missing ${OUTPUT_FILE#"${REPO_ROOT}/"}" >&2 + exit 1 + fi + fixture_canonical="$(canonical_crds "${OUTPUT_FILE}")" + if [[ "${fixture_canonical}" != "${upstream_canonical}" ]]; then + echo "Fixture CRDs differ from operator-controller ${OPERATOR_CONTROLLER_VERSION}." >&2 + echo "Run ./hack/fetch-olm-v1-api-crds.sh and commit the result." >&2 + exit 1 + fi + echo "Fixture matches operator-controller ${OPERATOR_CONTROLLER_VERSION} CRDs." + exit 0 +fi + +write_fixture "${OUTPUT_FILE}" +echo "Wrote ${OUTPUT_FILE#"${REPO_ROOT}/"} (sha256(crds)=${sha256})" From d6b29082d62586ad2e508bb525003f30d08a099f Mon Sep 17 00:00:00 2001 From: Fortune-Ndlovu Date: Thu, 24 Sep 2026 10:32:08 +0100 Subject: [PATCH 4/7] fix(orchestrator-infra): address shellcheck in OLM v1 CRD fetch script (#RHIDP-17168) Assign function positional args to locals and drop redundant yq on --check. Signed-off-by: Fortune Ndlovu --- hack/fetch-olm-v1-api-crds.sh | 48 ++++++++++++++++------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/hack/fetch-olm-v1-api-crds.sh b/hack/fetch-olm-v1-api-crds.sh index 9cec250b..96b214d4 100755 --- a/hack/fetch-olm-v1-api-crds.sh +++ b/hack/fetch-olm-v1-api-crds.sh @@ -15,7 +15,7 @@ Usage: $(basename "$0") [--check] Fetches OLM v1 API CRDs (CustomResourceDefinition only) from operator-controller ${OPERATOR_CONTROLLER_VERSION} and writes ${OUTPUT_FILE#"${REPO_ROOT}/"}. - --check Regenerate to a temp file and fail if it differs from the committed fixture. + --check Fail if the committed fixture CRDs differ from upstream. Environment: OPERATOR_CONTROLLER_VERSION Release tag (default: v1.11.0) @@ -35,41 +35,23 @@ if ! command -v yq >/dev/null 2>&1; then exit 1 fi +canonical_crds() { + local source_file=$1 + yq -o=json -I=0 ea 'select(.kind == "CustomResourceDefinition")' "${source_file}" +} + tmpdir="$(mktemp -d)" trap 'rm -rf "${tmpdir}"' EXIT curl -fsSL --proto '=https' "${UPSTREAM_URL}" -o "${tmpdir}/operator-controller.yaml" -yq ea 'select(.kind == "CustomResourceDefinition")' "${tmpdir}/operator-controller.yaml" \ - > "${tmpdir}/crds.yaml" - -canonical_crds() { - yq -o=json -I=0 ea 'select(.kind == "CustomResourceDefinition")' "$1" -} - upstream_canonical="$(canonical_crds "${tmpdir}/operator-controller.yaml")" -sha256="$(printf '%s' "${upstream_canonical}" | sha256sum | awk '{print $1}')" - -write_fixture() { - local dest=$1 - { - cat < "${dest}" -} if [[ "${check_only}" == true ]]; then if [[ ! -f "${OUTPUT_FILE}" ]]; then echo "Missing ${OUTPUT_FILE#"${REPO_ROOT}/"}" >&2 exit 1 fi - fixture_canonical="$(canonical_crds "${OUTPUT_FILE}")" - if [[ "${fixture_canonical}" != "${upstream_canonical}" ]]; then + if [[ "$(canonical_crds "${OUTPUT_FILE}")" != "${upstream_canonical}" ]]; then echo "Fixture CRDs differ from operator-controller ${OPERATOR_CONTROLLER_VERSION}." >&2 echo "Run ./hack/fetch-olm-v1-api-crds.sh and commit the result." >&2 exit 1 @@ -78,5 +60,19 @@ if [[ "${check_only}" == true ]]; then exit 0 fi -write_fixture "${OUTPUT_FILE}" +yq ea 'select(.kind == "CustomResourceDefinition")' "${tmpdir}/operator-controller.yaml" \ + > "${tmpdir}/crds.yaml" +sha256="$(printf '%s' "${upstream_canonical}" | sha256sum | awk '{print $1}')" + +{ + cat < "${OUTPUT_FILE}" + echo "Wrote ${OUTPUT_FILE#"${REPO_ROOT}/"} (sha256(crds)=${sha256})" From 8f3b4980fc0d9c1c34498b362125c016870e2c5c Mon Sep 17 00:00:00 2001 From: Fortune-Ndlovu Date: Thu, 24 Sep 2026 10:42:12 +0100 Subject: [PATCH 5/7] refactor(orchestrator-infra): trim duplicate CI checks and fixture docs (#RHIDP-17168) Document OLM v1 KinD CRDs in the chart README like Knative CRDs, drop .github/fixtures/README.md, remove redundant helm template CI step, and simplify the fetch script now that ct lint/install cover validation. Signed-off-by: Fortune Ndlovu --- .github/actions/test-charts/action.yml | 13 ------ .github/fixtures/README.md | 19 --------- .github/fixtures/olm-v1-api-crds.yaml | 4 +- charts/orchestrator-infra/README.md | 12 ++++++ charts/orchestrator-infra/README.md.gotmpl | 12 ++++++ hack/fetch-olm-v1-api-crds.sh | 49 +++------------------- 6 files changed, 31 insertions(+), 78 deletions(-) delete mode 100644 .github/fixtures/README.md diff --git a/.github/actions/test-charts/action.yml b/.github/actions/test-charts/action.yml index 7507f26b..0fb61680 100644 --- a/.github/actions/test-charts/action.yml +++ b/.github/actions/test-charts/action.yml @@ -121,19 +121,6 @@ runs: --values "$VALUES_FILE" 2>&1) || { echo "$output"; exit 1; } done - - name: Helm template sanity check (orchestrator-infra ci values) - if: steps.list-changed.outputs.changed == 'true' && inputs.chart == 'charts/orchestrator-infra' - shell: bash - env: - INPUT_CHART: ${{ inputs.chart }} - run: | - for ci_values in "$INPUT_CHART"/ci/*-values.yaml; do - [[ -f "$ci_values" ]] || continue - echo "==> helm template: $(basename "$ci_values")" - output=$(helm template test-release "$INPUT_CHART" --values "$ci_values" 2>&1) || { echo "$output"; exit 1; } - echo "$output" | grep -E '^kind:' | sort | uniq -c - done - - name: Generate KinD Config if: steps.list-changed.outputs.changed == 'true' shell: bash diff --git a/.github/fixtures/README.md b/.github/fixtures/README.md deleted file mode 100644 index a31c2fd2..00000000 --- a/.github/fixtures/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# CI fixtures - -## OLM v1 API CRDs (`olm-v1-api-crds.yaml`) - -KinD chart-testing for `charts/orchestrator-infra` with `olmVersion: v1` needs the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so `ClusterExtension` manifests can be admitted. The full `operator-controller` is not installed in CI—only these CRD definitions. - -**Update when bumping OLM v1 API version:** - -1. Set `OPERATOR_CONTROLLER_VERSION` if needed (default `v1.11.0` in the script). -2. From the repo root: `./hack/fetch-olm-v1-api-crds.sh` -3. Commit the updated `olm-v1-api-crds.yaml`. - -**Verify without writing:** - -```bash -./hack/fetch-olm-v1-api-crds.sh --check -``` - -The script downloads the upstream release manifest, keeps `CustomResourceDefinition` objects only, and records a `sha256(crds)` line in the file header. diff --git a/.github/fixtures/olm-v1-api-crds.yaml b/.github/fixtures/olm-v1-api-crds.yaml index 46748b61..62aa0e31 100644 --- a/.github/fixtures/olm-v1-api-crds.yaml +++ b/.github/fixtures/olm-v1-api-crds.yaml @@ -1,8 +1,8 @@ # OLM v1 API CRDs for orchestrator-infra chart-testing (olmVersion=v1). # Source: operator-framework/operator-controller v1.11.0 (CRDs only; controller not installed). -# Regenerate: ./hack/fetch-olm-v1-api-crds.sh +# Regenerate from repo root: hack/fetch-olm-v1-api-crds.sh +# See charts/orchestrator-infra/README.md — "OLM v1 API CRDs (chart-testing CI)". # Upstream: https://github.com/operator-framework/operator-controller/releases/tag/v1.11.0 -# sha256(crds): 79cf2241267b6ca9a35da5dd64beadd6d8a8f5c371405251a204392f3112924c apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index 6f135426..13c6ae5a 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -134,3 +134,15 @@ podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1b podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1beta1_knativeserving_crd.yaml > crds/knative-serving/knative-serving-crd.yaml ``` + +### OLM v1 API CRDs (chart-testing CI) + +KinD chart-testing with `olmVersion: v1` (see `ci/upstream-olm-v1-values.yaml`) requires the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so the chart's `ClusterExtension` resources can be admitted. CI applies `.github/fixtures/olm-v1-api-crds.yaml` (CRD definitions only; `operator-controller` is not installed). + +To refresh that fixture after bumping the OLM v1 API version, set `OPERATOR_CONTROLLER_VERSION` if needed (default `v1.11.0` in the script) and run from the repository root: + +```bash +./hack/fetch-olm-v1-api-crds.sh +``` + +Then commit `.github/fixtures/olm-v1-api-crds.yaml`. diff --git a/charts/orchestrator-infra/README.md.gotmpl b/charts/orchestrator-infra/README.md.gotmpl index 17e56993..1ffb2750 100644 --- a/charts/orchestrator-infra/README.md.gotmpl +++ b/charts/orchestrator-infra/README.md.gotmpl @@ -103,3 +103,15 @@ podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1b podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1beta1_knativeserving_crd.yaml > crds/knative-serving/knative-serving-crd.yaml ``` + +### OLM v1 API CRDs (chart-testing CI) + +KinD chart-testing with `olmVersion: v1` (see `ci/upstream-olm-v1-values.yaml`) requires the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so the chart's `ClusterExtension` resources can be admitted. CI applies `.github/fixtures/olm-v1-api-crds.yaml` (CRD definitions only; `operator-controller` is not installed). + +To refresh that fixture after bumping the OLM v1 API version, set `OPERATOR_CONTROLLER_VERSION` if needed (default `v1.11.0` in the script) and run from the repository root: + +```bash +./hack/fetch-olm-v1-api-crds.sh +``` + +Then commit `.github/fixtures/olm-v1-api-crds.yaml`. diff --git a/hack/fetch-olm-v1-api-crds.sh b/hack/fetch-olm-v1-api-crds.sh index 96b214d4..cb7791cf 100755 --- a/hack/fetch-olm-v1-api-crds.sh +++ b/hack/fetch-olm-v1-api-crds.sh @@ -8,25 +8,8 @@ REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" OUTPUT_FILE="${REPO_ROOT}/.github/fixtures/olm-v1-api-crds.yaml" UPSTREAM_URL="https://github.com/operator-framework/operator-controller/releases/download/${OPERATOR_CONTROLLER_VERSION}/operator-controller.yaml" -usage() { - cat <&2 +if [[ -n "${1:-}" ]]; then + echo "Usage: $(basename "$0")" >&2 exit 2 fi @@ -35,44 +18,22 @@ if ! command -v yq >/dev/null 2>&1; then exit 1 fi -canonical_crds() { - local source_file=$1 - yq -o=json -I=0 ea 'select(.kind == "CustomResourceDefinition")' "${source_file}" -} - tmpdir="$(mktemp -d)" trap 'rm -rf "${tmpdir}"' EXIT curl -fsSL --proto '=https' "${UPSTREAM_URL}" -o "${tmpdir}/operator-controller.yaml" -upstream_canonical="$(canonical_crds "${tmpdir}/operator-controller.yaml")" - -if [[ "${check_only}" == true ]]; then - if [[ ! -f "${OUTPUT_FILE}" ]]; then - echo "Missing ${OUTPUT_FILE#"${REPO_ROOT}/"}" >&2 - exit 1 - fi - if [[ "$(canonical_crds "${OUTPUT_FILE}")" != "${upstream_canonical}" ]]; then - echo "Fixture CRDs differ from operator-controller ${OPERATOR_CONTROLLER_VERSION}." >&2 - echo "Run ./hack/fetch-olm-v1-api-crds.sh and commit the result." >&2 - exit 1 - fi - echo "Fixture matches operator-controller ${OPERATOR_CONTROLLER_VERSION} CRDs." - exit 0 -fi - yq ea 'select(.kind == "CustomResourceDefinition")' "${tmpdir}/operator-controller.yaml" \ > "${tmpdir}/crds.yaml" -sha256="$(printf '%s' "${upstream_canonical}" | sha256sum | awk '{print $1}')" { cat < "${OUTPUT_FILE}" -echo "Wrote ${OUTPUT_FILE#"${REPO_ROOT}/"} (sha256(crds)=${sha256})" +echo "Wrote ${OUTPUT_FILE#"${REPO_ROOT}/"}" From f9e3e9ca0eccf27aaf471a9b064b694a8a066e89 Mon Sep 17 00:00:00 2001 From: Fortune-Ndlovu Date: Thu, 24 Sep 2026 10:47:35 +0100 Subject: [PATCH 6/7] refactor(orchestrator-infra): fetch OLM v1 CRDs in CI (#RHIDP-17168) Apply Armel's suggestion: pin OPERATOR_CONTROLLER_VERSION in test-charts, download operator-controller release CRDs at KinD setup, and drop the vendored fixture and fetch script. Signed-off-by: Fortune Ndlovu --- .github/actions/test-charts/action.yml | 6 +- .github/fixtures/olm-v1-api-crds.yaml | 1174 -------------------- charts/orchestrator-infra/README.md | 10 +- charts/orchestrator-infra/README.md.gotmpl | 10 +- hack/fetch-olm-v1-api-crds.sh | 39 - 5 files changed, 9 insertions(+), 1230 deletions(-) delete mode 100644 .github/fixtures/olm-v1-api-crds.yaml delete mode 100755 hack/fetch-olm-v1-api-crds.sh diff --git a/.github/actions/test-charts/action.yml b/.github/actions/test-charts/action.yml index 0fb61680..f4951c15 100644 --- a/.github/actions/test-charts/action.yml +++ b/.github/actions/test-charts/action.yml @@ -206,8 +206,12 @@ runs: - name: Install OLM v1 API CRDs (orchestrator-infra chart-testing) if: steps.list-changed.outputs.orchestratorOlmV1CrdsNeeded == 'true' shell: bash + env: + OPERATOR_CONTROLLER_VERSION: "v1.11.0" run: | - kubectl create -f .github/fixtures/olm-v1-api-crds.yaml + url="https://github.com/operator-framework/operator-controller/releases/download/${OPERATOR_CONTROLLER_VERSION}/operator-controller.yaml" + curl -fsSL --proto '=https' "${url}" -o /tmp/operator-controller.yaml + yq ea 'select(.kind == "CustomResourceDefinition")' /tmp/operator-controller.yaml | kubectl create -f - - name: Set up external services and test resources for rhdh if: steps.list-changed.outputs.externalDbNeeded == 'true' diff --git a/.github/fixtures/olm-v1-api-crds.yaml b/.github/fixtures/olm-v1-api-crds.yaml deleted file mode 100644 index 62aa0e31..00000000 --- a/.github/fixtures/olm-v1-api-crds.yaml +++ /dev/null @@ -1,1174 +0,0 @@ -# OLM v1 API CRDs for orchestrator-infra chart-testing (olmVersion=v1). -# Source: operator-framework/operator-controller v1.11.0 (CRDs only; controller not installed). -# Regenerate from repo root: hack/fetch-olm-v1-api-crds.sh -# See charts/orchestrator-infra/README.md — "OLM v1 API CRDs (chart-testing CI)". -# Upstream: https://github.com/operator-framework/operator-controller/releases/tag/v1.11.0 -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.20.1 - olm.operatorframework.io/generator: standard - name: clustercatalogs.olm.operatorframework.io -spec: - group: olm.operatorframework.io - names: - kind: ClusterCatalog - listKind: ClusterCatalogList - plural: clustercatalogs - singular: clustercatalog - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .status.lastUnpacked - name: LastUnpacked - type: date - - jsonPath: .status.conditions[?(@.type=="Serving")].status - name: Serving - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: 'ClusterCatalog makes File-Based Catalog (FBC) data available - to your cluster. - - For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs' - 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: 'spec is a required field that defines the desired state - of the ClusterCatalog. - - The controller ensures that the catalog is unpacked and served over - the catalog content HTTP server.' - properties: - availabilityMode: - default: Available - description: 'availabilityMode is an optional field that defines how - the ClusterCatalog is made available to clients on the cluster. - - - Allowed values are "Available", "Unavailable", or omitted. - - - When omitted, the default value is "Available". - - - When set to "Available", the catalog contents are unpacked and served - over the catalog content HTTP server. - - Clients should consider this ClusterCatalog and its contents as - usable. - - - When set to "Unavailable", the catalog contents are no longer served - over the catalog content HTTP server. - - Treat this the same as if the ClusterCatalog does not exist. - - Use "Unavailable" when you want to keep the ClusterCatalog but treat - it as if it doesn''t exist.' - enum: - - Unavailable - - Available - type: string - priority: - default: 0 - description: 'priority is an optional field that defines a priority - for this ClusterCatalog. - - - Clients use the ClusterCatalog priority as a tie-breaker between - ClusterCatalogs that meet their requirements. - - Higher numbers mean higher priority. - - - Clients decide how to handle scenarios where multiple ClusterCatalogs - with the same priority meet their requirements. - - Clients should prompt users for additional input to break the tie. - - - When omitted, the default priority is 0. - - - Use negative numbers to specify a priority lower than the default. - - Use positive numbers to specify a priority higher than the default. - - - The lowest possible value is -2147483648. - - The highest possible value is 2147483647.' - format: int32 - maximum: 2147483647 - minimum: -2147483648 - type: integer - source: - description: "source is a required field that defines the source of\ - \ a catalog.\nA catalog contains information on content that can\ - \ be installed on a cluster.\nThe catalog source makes catalog contents\ - \ discoverable and usable by other on-cluster components.\nThese\ - \ components can present the content in a GUI dashboard or install\ - \ content from the catalog on the cluster.\nThe catalog source must\ - \ contain catalog metadata in the File-Based Catalog (FBC) format.\n\ - For more information on FBC, see https://olm.operatorframework.io/docs/reference/file-based-catalogs/#docs.\n\ - \nBelow is a minimal example of a ClusterCatalogSpec that sources\ - \ a catalog from an image:\n\n source:\n type: Image\n image:\n\ - \ ref: quay.io/operatorhubio/catalog:latest" - properties: - image: - description: 'image configures how catalog contents are sourced - from an OCI image. - - It is required when type is Image, and forbidden otherwise.' - properties: - pollIntervalMinutes: - description: 'pollIntervalMinutes is an optional field that - sets the interval, in minutes, at which the image source - is polled for new content. - - You cannot specify pollIntervalMinutes when ref is a digest-based - reference. - - - When omitted, the image is not polled for new content.' - minimum: 1 - type: integer - ref: - description: 'ref is a required field that defines the reference - to a container image containing catalog contents. - - It cannot be more than 1000 characters. - - - A reference has 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 - type: string - x-kubernetes-validations: - - message: must start with a valid domain. valid domains must - be alphanumeric characters (lowercase and uppercase) separated - by the "." character. - rule: self.matches('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])((\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(:[0-9]+)?\\b') - - message: a valid name is required. valid names must contain - lowercase alphanumeric characters separated only by the - ".", "_", "__", "-" characters. - rule: self.find('(\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)') - != "" - - message: must end with a digest or a tag - rule: self.find('(@.*:)') != "" || self.find(':.*$') != - "" - - message: tag is invalid. the tag must not be more than 127 - characters - rule: 'self.find(''(@.*:)'') == "" ? (self.find('':.*$'') - != "" ? self.find('':.*$'').substring(1).size() <= 127 - : true) : true' - - message: tag is invalid. valid tags must begin with a word - character (alphanumeric + "_") followed by word characters - or ".", and "-" characters - rule: 'self.find(''(@.*:)'') == "" ? (self.find('':.*$'') - != "" ? self.find('':.*$'').matches('':[\\w][\\w.-]*$'') - : true) : true' - - message: digest algorithm is not valid. valid algorithms - must start with an uppercase or lowercase alpha character - followed by alphanumeric characters and may contain the - "-", "_", "+", and "." characters. - rule: 'self.find(''(@.*:)'') != "" ? self.find(''(@.*:)'').matches(''(@[A-Za-z][A-Za-z0-9]*([-_+.][A-Za-z][A-Za-z0-9]*)*[:])'') - : true' - - message: digest is not valid. the encoded string must be - at least 32 characters - rule: 'self.find(''(@.*:)'') != "" ? self.find('':.*$'').substring(1).size() - >= 32 : true' - - message: digest is not valid. the encoded string must only - contain hex characters (A-F, a-f, 0-9) - rule: 'self.find(''(@.*:)'') != "" ? self.find('':.*$'').matches('':[0-9A-Fa-f]*$'') - : true' - required: - - ref - type: object - x-kubernetes-validations: - - message: cannot specify pollIntervalMinutes while using digest-based - image - rule: 'self.ref.find(''(@.*:)'') != "" ? !has(self.pollIntervalMinutes) - : true' - type: - description: 'type is a required field that specifies the type - of source for the catalog. - - - The only allowed value is "Image". - - - When set to "Image", the ClusterCatalog content is 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 - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: image is required when source type is Image, and forbidden - otherwise - rule: 'has(self.type) && self.type == ''Image'' ? has(self.image) - : !has(self.image)' - required: - - source - type: object - status: - description: "status contains the following information about the state\ - \ of the ClusterCatalog:\n - Whether the catalog contents are being\ - \ served via the catalog content HTTP server\n - Whether the ClusterCatalog\ - \ is progressing to a new state\n - A reference to the source from\ - \ which the catalog contents were retrieved" - properties: - conditions: - description: "conditions represents the current state of this ClusterCatalog.\n\ - \nThe current condition types are Serving and Progressing.\n\nThe\ - \ Serving condition represents whether the catalog contents are\ - \ being served via the HTTP(S) web server:\n - When status is True\ - \ and reason is Available, the catalog contents are being served.\n\ - \ - When status is False and reason is Unavailable, the catalog\ - \ contents are not being served because the contents are not yet\ - \ available.\n - When status is False and reason is UserSpecifiedUnavailable,\ - \ the catalog contents are not being served because the catalog\ - \ has been intentionally marked as unavailable.\n\nThe Progressing\ - \ condition represents whether the ClusterCatalog is progressing\ - \ or is ready to progress towards a new state:\n - When status\ - \ is True and reason is Retrying, an error occurred that may be\ - \ resolved on subsequent reconciliation attempts.\n - When status\ - \ is True and reason is Succeeded, the ClusterCatalog has successfully\ - \ progressed to a new state and is ready to continue progressing.\n\ - \ - When status is False and reason is Blocked, an error occurred\ - \ that requires manual intervention for recovery.\n\nIf the system\ - \ initially fetched contents and polling identifies updates, both\ - \ conditions can be active simultaneously:\n - The Serving condition\ - \ remains True with reason Available because the previous contents\ - \ are still served via the HTTP(S) web server.\n - The Progressing\ - \ condition is True with reason Retrying because the system is working\ - \ to serve the new version." - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: 'lastTransitionTime is the last time the condition - transitioned from one status to another. - - This should be when the underlying condition changed. If - that is not known, then using the time when the API field - changed is acceptable.' - format: date-time - type: string - message: - description: 'message is a human readable message indicating - details about the transition. - - This may be an empty string.' - maxLength: 32768 - type: string - observedGeneration: - description: 'observedGeneration represents the .metadata.generation - that the condition was set based upon. - - For instance, if .metadata.generation is currently 12, but - the .status.conditions[x].observedGeneration is 9, the condition - is out of date - - with respect to the current state of the instance.' - format: int64 - minimum: 0 - type: integer - reason: - description: 'reason contains a programmatic identifier indicating - the reason for the condition''s last transition. - - Producers of specific condition types may define expected - values and meanings for this field, - - and whether the values are considered a guaranteed API. - - The value should be a CamelCase string. - - This field may not be empty.' - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - 'True' - - 'False' - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - lastUnpacked: - description: 'lastUnpacked represents the last time the catalog contents - were extracted from their source format. - - For example, when using an Image source, the OCI image is pulled - and image layers are written to a file-system backed cache. - - This extraction from the source format is called "unpacking".' - format: date-time - type: string - resolvedSource: - description: resolvedSource contains information about the resolved - source based on the source type. - properties: - image: - description: 'image contains resolution information for a catalog - sourced from an image. - - It must be set when type is Image, and forbidden otherwise.' - properties: - ref: - description: 'ref contains the resolved image digest-based - reference. - - The digest format allows you to use other tooling to fetch - the exact OCI manifests - - that were used to extract the catalog contents.' - maxLength: 1000 - type: string - x-kubernetes-validations: - - message: must start with a valid domain. valid domains must - be alphanumeric characters (lowercase and uppercase) separated - by the "." character. - rule: self.matches('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])((\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(:[0-9]+)?\\b') - - message: a valid name is required. valid names must contain - lowercase alphanumeric characters separated only by the - ".", "_", "__", "-" characters. - rule: self.find('(\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)') - != "" - - message: must end with a digest - rule: self.find('(@.*:)') != "" - - message: digest algorithm is not valid. valid algorithms - must start with an uppercase or lowercase alpha character - followed by alphanumeric characters and may contain the - "-", "_", "+", and "." characters. - rule: 'self.find(''(@.*:)'') != "" ? self.find(''(@.*:)'').matches(''(@[A-Za-z][A-Za-z0-9]*([-_+.][A-Za-z][A-Za-z0-9]*)*[:])'') - : true' - - message: digest is not valid. the encoded string must be - at least 32 characters - rule: 'self.find(''(@.*:)'') != "" ? self.find('':.*$'').substring(1).size() - >= 32 : true' - - message: digest is not valid. the encoded string must only - contain hex characters (A-F, a-f, 0-9) - rule: 'self.find(''(@.*:)'') != "" ? self.find('':.*$'').matches('':[0-9A-Fa-f]*$'') - : true' - required: - - ref - type: object - type: - description: 'type is a required field that specifies the type - of source for the catalog. - - - The only allowed value is "Image". - - - When set to "Image", information about the resolved image source - is set in the image field.' - enum: - - Image - type: string - required: - - image - - type - type: object - x-kubernetes-validations: - - message: image is required when source type is Image, and forbidden - otherwise - rule: 'has(self.type) && self.type == ''Image'' ? has(self.image) - : !has(self.image)' - urls: - description: urls contains the URLs that can be used to access the - catalog. - properties: - base: - description: "base is a cluster-internal URL that provides endpoints\ - \ for accessing the catalog content.\n\nClients should append\ - \ the path for the endpoint they want to access.\n\nCurrently,\ - \ only a single endpoint is served and is accessible at the\ - \ path /api/v1.\n\nThe endpoints served for the v1 API are:\n\ - \ - /all - this endpoint returns the entire catalog contents\ - \ in the FBC format\n\nNew endpoints may be added as needs evolve." - maxLength: 525 - type: string - x-kubernetes-validations: - - message: must be a valid URL - rule: isURL(self) - - message: scheme must be either http or https - rule: 'isURL(self) ? (url(self).getScheme() == "http" || url(self).getScheme() - == "https") : true' - required: - - base - type: object - type: object - required: - - metadata - - spec - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.21.0 - olm.operatorframework.io/generator: standard - name: clusterextensions.olm.operatorframework.io -spec: - group: olm.operatorframework.io - names: - kind: ClusterExtension - listKind: ClusterExtensionList - plural: clusterextensions - singular: clusterextension - scope: Cluster - versions: - - 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: v1 - schema: - openAPIV3Schema: - description: ClusterExtension is the Schema for the clusterextensions API - 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: spec is an optional field that defines the desired state - of the ClusterExtension. - properties: - install: - description: 'install is optional and configures installation options - for the ClusterExtension, - - such as the pre-flight check configuration.' - properties: - preflight: - description: 'preflight is optional and configures the checks - that 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 is used.' - properties: - crdUpgradeSafety: - description: 'crdUpgradeSafety configures the CRD Upgrade - Safety pre-flight checks that run - - before upgrades of installed content. - - - The CRD Upgrade Safety pre-flight check safeguards from - unintended consequences of upgrading a CRD, - - such as data loss.' - properties: - enforcement: - description: 'enforcement is required and configures 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 is skipped during an upgrade operation. - - Use this option with caution as unintended consequences - such as data loss can occur. - - - When set to "Strict", the CRD Upgrade Safety pre-flight - check runs during an upgrade operation.' - enum: - - None - - Strict - type: string - required: - - enforcement - type: object - required: - - crdUpgradeSafety - type: object - x-kubernetes-validations: - - message: at least one of [crdUpgradeSafety] are required when - preflight is specified - rule: has(self.crdUpgradeSafety) - type: object - x-kubernetes-validations: - - message: at least one of [preflight] are required when install is - specified - rule: has(self.preflight) - namespace: - description: 'namespace specifies a Kubernetes namespace. - - It 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. - - - The namespace field 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 deprecated field and is completely - ignored. - - OLMv1 is a single-tenant system where users with ClusterExtension - write access are - - effectively delegated cluster-admin trust. The operator-controller - runs with - - cluster-admin privileges and uses its own service account for all - cluster interactions. - - - Deprecated: serviceAccount is no longer used and will be removed - in a future release.' - minProperties: 1 - properties: - name: - description: "name is a deprecated field and is completely ignored.\n\ - \nDeprecated: name is no longer used and will be removed in\ - \ a future release.\n\nThe name field follows the DNS subdomain\ - \ standard as defined in [RFC 1123].\nIt must contain only lowercase\ - \ alphanumeric characters, hyphens (-) or periods (.),\nstart\ - \ and end with an alphanumeric character, and be no longer than\ - \ 253 characters.\n\nSome examples of valid values are:\n -\ - \ some-serviceaccount\n - 123-serviceaccount\n - 1-serviceaccount-2\n\ - \ - someserviceaccount\n - some.serviceaccount\n\nSome examples\ - \ of invalid values are:\n - -some-serviceaccount\n - some-serviceaccount-\n\ - \n[RFC 1123]: https://tools.ietf.org/html/rfc1123" - maxLength: 253 - minLength: 1 - type: string - x-kubernetes-validations: - - message: name is immutable once set but may be cleared - 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])?)*$") - type: object - source: - description: "source is required and selects the installation source\ - \ of content for this ClusterExtension.\nSet the sourceType field\ - \ to perform the selection.\n\nCatalog is currently the only implemented\ - \ sourceType.\nSetting sourceType to \"Catalog\" requires the catalog\ - \ field to also be defined.\n\nBelow is a minimal example of a source\ - \ definition (in yaml):\n\nsource:\n sourceType: Catalog\n catalog:\n\ - \ packageName: example-package" - properties: - catalog: - description: 'catalog configures how information is sourced from - a catalog. - - It is required when sourceType is "Catalog", and forbidden otherwise.' - properties: - channels: - description: "channels is optional and specifies a set of\ - \ channels belonging to the package\nspecified in the packageName\ - \ field.\n\nA channel is a package-author-defined stream\ - \ of updates for an extension.\n\nEach channel in the list\ - \ must follow the DNS subdomain standard as defined in [RFC\ - \ 1123].\nIt must contain only lowercase alphanumeric characters,\ - \ hyphens (-) or periods (.),\nstart and end with an alphanumeric\ - \ character, and be no longer than 253 characters.\nYou\ - \ can specify no more than 256 channels.\n\nWhen specified,\ - \ it constrains the set of installable bundles and the automated\ - \ upgrade path.\nThis constraint is an AND operation with\ - \ the version field. For example:\n - Given channel is\ - \ set to \"foo\"\n - Given version is set to \">=1.0.0,\ - \ <1.5.0\"\n - Only bundles that exist in channel \"foo\"\ - \ AND satisfy the version range comparison are considered\ - \ installable\n - Automatic upgrades are constrained to\ - \ upgrade edges defined by the selected channel\n\nWhen\ - \ unspecified, upgrade edges across all channels are used\ - \ to identify valid automatic upgrade paths.\n\nSome examples\ - \ of valid values are:\n - 1.1.x\n - alpha\n - stable\n\ - \ - stable-v1\n - v1-stable\n - dev-preview\n - preview\n\ - \ - community\n\nSome examples of invalid values are:\n\ - \ - -some-channel\n - some-channel-\n - thisisareallylongchannelnamethatisgreaterthanthemaximumlength\n\ - \ - original_40\n - --default-channel\n\n[RFC 1123]: https://tools.ietf.org/html/rfc1123" - items: - maxLength: 253 - 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 specifies the name of the package\ - \ to be installed and is used to filter\nthe content from\ - \ catalogs.\n\nIt is required, immutable, and follows the\ - \ DNS subdomain standard as defined in [RFC 1123].\nIt must\ - \ contain only lowercase alphanumeric characters, hyphens\ - \ (-) or periods (.),\nstart and end with an alphanumeric\ - \ character, and be no longer than 253 characters.\n\nSome\ - \ examples of valid values are:\n - some-package\n - 123-package\n\ - \ - 1-package-2\n - somepackage\n\nSome examples of invalid\ - \ values are:\n - -some-package\n - some-package-\n -\ - \ thisisareallylongpackagenamethatisgreaterthanthemaximumlength\n\ - \ - some.package\n\n[RFC 1123]: https://tools.ietf.org/html/rfc1123" - maxLength: 253 - 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 optional and filters the set of - ClusterCatalogs used in the bundle selection process. - - - When unspecified, all ClusterCatalogs are used in the bundle - selection process.' - 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 - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - 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 - upgradeConstraintPolicy: - default: CatalogProvided - description: 'upgradeConstraintPolicy is optional and controls - whether the upgrade paths defined in the catalog - - are enforced for the package referenced in the packageName - field. - - - Allowed values are "CatalogProvided", "SelfCertified", or - omitted. - - - When set to "CatalogProvided", automatic upgrades only occur - when upgrade constraints specified by the package - - author are met. - - - When set to "SelfCertified", the upgrade constraints specified - by the package author are ignored. - - This allows 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. - - Use this option only if you have independently verified - the changes. - - - When omitted, the default value is "CatalogProvided".' - enum: - - CatalogProvided - - SelfCertified - type: string - version: - description: "version is an optional semver constraint (a\ - \ specific version or range of versions).\nWhen unspecified,\ - \ the latest version available is installed.\n\nAcceptable\ - \ version ranges are no longer than 64 characters.\nVersion\ - \ ranges are composed of comma- or space-delimited values\ - \ and one or more comparison operators,\nknown as comparison\ - \ strings.\nYou can add additional comparison strings using\ - \ the OR operator (||).\n\n# Range Comparisons\n\nTo specify\ - \ a version range, you can use a comparison string like\ - \ \">=3.0,\n<3.6\". When specifying a range, automatic updates\ - \ will occur within that\nrange. The example comparison\ - \ string means \"install any version greater than\nor equal\ - \ to 3.0.0 but less than 3.6.0.\". It also states intent\ - \ that if any\nupgrades are available within the version\ - \ range after initial installation,\nthose upgrades should\ - \ be automatically performed.\n\n# Pinned Versions\n\nTo\ - \ specify an exact version to install you can use a version\ - \ range that\n\"pins\" to a specific version. When pinning\ - \ to a specific version, no\nautomatic updates will occur.\ - \ An example of a pinned version range is\n\"0.6.0\", which\ - \ means \"only install version 0.6.0 and never\nupgrade\ - \ from this version\".\n\n# Basic Comparison Operators\n\ - \nThe basic comparison operators and their meanings are:\n\ - \ - \"=\", equal (not aliased to an operator)\n - \"!=\"\ - , not equal\n - \"<\", less than\n - \">\", greater than\n\ - \ - \">=\", greater than OR equal to\n - \"<=\", less\ - \ than OR equal to\n\n# Wildcard Comparisons\n\nYou can\ - \ use the \"x\", \"X\", and \"*\" characters as wildcard\ - \ characters in all\ncomparison operations. Some examples\ - \ of using the wildcard characters:\n - \"1.2.x\", \"1.2.X\"\ - , and \"1.2.*\" is equivalent to \">=1.2.0, < 1.3.0\"\n\ - \ - \">= 1.2.x\", \">= 1.2.X\", and \">= 1.2.*\" is equivalent\ - \ to \">= 1.2.0\"\n - \"<= 2.x\", \"<= 2.X\", and \"<=\ - \ 2.*\" is equivalent to \"< 3\"\n - \"x\", \"X\", and\ - \ \"*\" is equivalent to \">= 0.0.0\"\n\n# Patch Release\ - \ Comparisons\n\nWhen you want to specify a minor version\ - \ up to the next major version you\ncan use the \"~\" character\ - \ to perform patch comparisons. Some examples:\n - \"~1.2.3\"\ - \ is equivalent to \">=1.2.3, <1.3.0\"\n - \"~1\" and \"\ - ~1.x\" is equivalent to \">=1, <2\"\n - \"~2.3\" is equivalent\ - \ to \">=2.3, <2.4\"\n - \"~1.2.x\" is equivalent to \"\ - >=1.2.0, <1.3.0\"\n\n# Major Release Comparisons\n\nYou\ - \ can use the \"^\" character to make major release comparisons\ - \ after a\nstable 1.0.0 version is published. If there is\ - \ no stable version published, // minor versions define\ - \ the stability level. Some examples:\n - \"^1.2.3\" is\ - \ equivalent to \">=1.2.3, <2.0.0\"\n - \"^1.2.x\" is equivalent\ - \ to \">=1.2.0, <2.0.0\"\n - \"^2.3\" is equivalent to\ - \ \">=2.3, <3\"\n - \"^2.x\" is equivalent to \">=2.0.0,\ - \ <3\"\n - \"^0.2.3\" is equivalent to \">=0.2.3, <0.3.0\"\ - \n - \"^0.2\" is equivalent to \">=0.2.0, <0.3.0\"\n -\ - \ \"^0.0.3\" is equvalent to \">=0.0.3, <0.0.4\"\n - \"\ - ^0.0\" is equivalent to \">=0.0.0, <0.1.0\"\n - \"^0\"\ - \ is equivalent to \">=0.0.0, <1.0.0\"\n\n# OR Comparisons\n\ - You can use the \"||\" character to represent an OR operation\ - \ in the version\nrange. Some examples:\n - \">=1.2.3,\ - \ <2.0.0 || >3.0.0\"\n - \"^0 || ^3 || ^5\"\n\nFor more\ - \ information on semver, please see https://semver.org/" - maxLength: 64 - 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 - sourceType: - description: 'sourceType is required and specifies the type of - install source. - - - The only allowed value is "Catalog". - - - When set to "Catalog", information for determining the appropriate - bundle of content to install - - is fetched from ClusterCatalog resources on the cluster. - - When using the Catalog sourceType, the catalog field must also - be set.' - enum: - - Catalog - type: string - required: - - sourceType - type: object - x-kubernetes-validations: - - 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: - - namespace - - source - type: object - status: - description: status is an optional field that defines the observed state - of the ClusterExtension. - properties: - conditions: - description: "conditions represents the current state of the ClusterExtension.\n\ - \nThe set of condition types which apply to all spec.source variations\ - \ are Installed and Progressing.\n\nThe Installed condition represents\ - \ whether the bundle has been installed for this ClusterExtension:\n\ - \ - When Installed is True and the Reason is Succeeded, the bundle\ - \ has been successfully installed.\n - When Installed is False\ - \ and the Reason is Failed, the bundle has failed to install.\n\n\ - The Progressing condition represents whether or not the ClusterExtension\ - \ is advancing towards a new state.\nWhen Progressing is True and\ - \ the Reason is Succeeded, the ClusterExtension is making progress\ - \ towards a new state.\nWhen Progressing is True and the Reason\ - \ is Retrying, the ClusterExtension has encountered an error that\ - \ could be resolved on subsequent reconciliation attempts.\nWhen\ - \ Progressing is False and the Reason is Blocked, the ClusterExtension\ - \ has encountered an error that requires manual intervention for\ - \ recovery.\n\nWhen the ClusterExtension is sourced from a catalog,\ - \ it surfaces deprecation conditions based on catalog metadata.\n\ - These are indications from a package owner to guide users away from\ - \ a particular package, channel, or bundle:\n - BundleDeprecated\ - \ is True if the installed bundle is marked deprecated, False if\ - \ not deprecated, or Unknown if no bundle is installed yet or if\ - \ catalog data is unavailable.\n - ChannelDeprecated is True if\ - \ any requested channel is marked deprecated, False if not deprecated,\ - \ or Unknown if catalog data is unavailable.\n - PackageDeprecated\ - \ is True if the requested package is marked deprecated, False if\ - \ not deprecated, or Unknown if catalog data is unavailable.\n \ - \ - Deprecated is a rollup condition that is True when any deprecation\ - \ exists, False when none exist, or Unknown when catalog data is\ - \ unavailable." - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: 'lastTransitionTime is the last time the condition - transitioned from one status to another. - - This should be when the underlying condition changed. If - that is not known, then using the time when the API field - changed is acceptable.' - format: date-time - type: string - message: - description: 'message is a human readable message indicating - details about the transition. - - This may be an empty string.' - maxLength: 32768 - type: string - observedGeneration: - description: 'observedGeneration represents the .metadata.generation - that the condition was set based upon. - - For instance, if .metadata.generation is currently 12, but - the .status.conditions[x].observedGeneration is 9, the condition - is out of date - - with respect to the current state of the instance.' - format: int64 - minimum: 0 - type: integer - reason: - description: 'reason contains a programmatic identifier indicating - the reason for the condition''s last transition. - - Producers of specific condition types may define expected - values and meanings for this field, - - and whether the values are considered a guaranteed API. - - The value should be a CamelCase string. - - This field may not be empty.' - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - 'True' - - 'False' - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - install: - description: install is a representation of the current installation - status for this ClusterExtension. - properties: - bundle: - description: 'bundle is required and 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 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 required and references the version - that this bundle represents. - - It 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 - type: object - required: - - bundle - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index 13c6ae5a..04b0f529 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -137,12 +137,6 @@ podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1b ### OLM v1 API CRDs (chart-testing CI) -KinD chart-testing with `olmVersion: v1` (see `ci/upstream-olm-v1-values.yaml`) requires the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so the chart's `ClusterExtension` resources can be admitted. CI applies `.github/fixtures/olm-v1-api-crds.yaml` (CRD definitions only; `operator-controller` is not installed). +KinD chart-testing with `olmVersion: v1` (see `ci/upstream-olm-v1-values.yaml`) requires the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so the chart's `ClusterExtension` resources can be admitted. CI downloads the pinned `operator-framework/operator-controller` release, keeps `CustomResourceDefinition` objects only, and applies them (`operator-controller` is not installed). -To refresh that fixture after bumping the OLM v1 API version, set `OPERATOR_CONTROLLER_VERSION` if needed (default `v1.11.0` in the script) and run from the repository root: - -```bash -./hack/fetch-olm-v1-api-crds.sh -``` - -Then commit `.github/fixtures/olm-v1-api-crds.yaml`. +Bump the OLM v1 API version used in chart-testing by updating `OPERATOR_CONTROLLER_VERSION` in the `Install OLM v1 API CRDs` step of [`.github/actions/test-charts/action.yml`](../../.github/actions/test-charts/action.yml). diff --git a/charts/orchestrator-infra/README.md.gotmpl b/charts/orchestrator-infra/README.md.gotmpl index 1ffb2750..d5e6f661 100644 --- a/charts/orchestrator-infra/README.md.gotmpl +++ b/charts/orchestrator-infra/README.md.gotmpl @@ -106,12 +106,6 @@ podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1b ### OLM v1 API CRDs (chart-testing CI) -KinD chart-testing with `olmVersion: v1` (see `ci/upstream-olm-v1-values.yaml`) requires the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so the chart's `ClusterExtension` resources can be admitted. CI applies `.github/fixtures/olm-v1-api-crds.yaml` (CRD definitions only; `operator-controller` is not installed). +KinD chart-testing with `olmVersion: v1` (see `ci/upstream-olm-v1-values.yaml`) requires the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so the chart's `ClusterExtension` resources can be admitted. CI downloads the pinned `operator-framework/operator-controller` release, keeps `CustomResourceDefinition` objects only, and applies them (`operator-controller` is not installed). -To refresh that fixture after bumping the OLM v1 API version, set `OPERATOR_CONTROLLER_VERSION` if needed (default `v1.11.0` in the script) and run from the repository root: - -```bash -./hack/fetch-olm-v1-api-crds.sh -``` - -Then commit `.github/fixtures/olm-v1-api-crds.yaml`. +Bump the OLM v1 API version used in chart-testing by updating `OPERATOR_CONTROLLER_VERSION` in the `Install OLM v1 API CRDs` step of [`.github/actions/test-charts/action.yml`](../../.github/actions/test-charts/action.yml). diff --git a/hack/fetch-olm-v1-api-crds.sh b/hack/fetch-olm-v1-api-crds.sh deleted file mode 100755 index cb7791cf..00000000 --- a/hack/fetch-olm-v1-api-crds.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -OPERATOR_CONTROLLER_VERSION="${OPERATOR_CONTROLLER_VERSION:-v1.11.0}" -SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" -OUTPUT_FILE="${REPO_ROOT}/.github/fixtures/olm-v1-api-crds.yaml" -UPSTREAM_URL="https://github.com/operator-framework/operator-controller/releases/download/${OPERATOR_CONTROLLER_VERSION}/operator-controller.yaml" - -if [[ -n "${1:-}" ]]; then - echo "Usage: $(basename "$0")" >&2 - exit 2 -fi - -if ! command -v yq >/dev/null 2>&1; then - echo "yq is required (see https://github.com/mikefarah/yq)" >&2 - exit 1 -fi - -tmpdir="$(mktemp -d)" -trap 'rm -rf "${tmpdir}"' EXIT - -curl -fsSL --proto '=https' "${UPSTREAM_URL}" -o "${tmpdir}/operator-controller.yaml" -yq ea 'select(.kind == "CustomResourceDefinition")' "${tmpdir}/operator-controller.yaml" \ - > "${tmpdir}/crds.yaml" - -{ - cat < "${OUTPUT_FILE}" - -echo "Wrote ${OUTPUT_FILE#"${REPO_ROOT}/"}" From 3dbcb6cfbee97311b87b6c492691fe41a60cd875 Mon Sep 17 00:00:00 2001 From: Fortune-Ndlovu Date: Thu, 24 Sep 2026 11:11:01 +0100 Subject: [PATCH 7/7] docs(orchestrator-infra): clarify OLM v1 CI CRD setup in README (#RHIDP-17168) Explain chart-testing vs OpenShift, KinD fetch behavior, and where to bump OPERATOR_CONTROLLER_VERSION for maintainers. Signed-off-by: Fortune Ndlovu --- charts/orchestrator-infra/README.md | 16 ++++++++++------ charts/orchestrator-infra/README.md.gotmpl | 16 ++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index 04b0f529..2b7516c2 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -119,6 +119,16 @@ helm install my-orchestrator-infra ./charts/orchestrator-infra --set olmVersion= helm install my-orchestrator-infra ./charts/orchestrator-infra --set olmVersion=v1 ``` +### OLM v1 API CRDs (chart-testing only) + +With `olmVersion: v1`, this chart creates `ClusterExtension` resources (see the previous section). Before Kubernetes can accept those objects, the cluster must already expose the OLM v1 API types `ClusterExtension` and `ClusterCatalog`. + +On OpenShift, those APIs usually come from the platform (you do not install them as part of this chart). The `rhdh-operator` plugin-infra flow assumes they are already present when it applies its own `ClusterExtension` manifests. + +Pull request CI tests this chart on a KinD cluster using `ci/upstream-olm-v1-values.yaml`. KinD does not ship OLM v1, so the shared test workflow downloads a pinned [operator-controller](https://github.com/operator-framework/operator-controller) release, applies only the `CustomResourceDefinition` manifests from that bundle, and does **not** install the operator-controller controller. That is enough for `helm template`, `ct install`, and `helm test` to validate the v1 chart output. + +To change which operator-controller release CI uses, update `OPERATOR_CONTROLLER_VERSION` in the `Install OLM v1 API CRDs (orchestrator-infra chart-testing)` step in [`.github/actions/test-charts/action.yml`](../../.github/actions/test-charts/action.yml). + ### Installing Knative Eventing and Knative Serving CRDs The orchestrator-infra chart requires several CRDs for Knative Eventing and Knative Serving. These CRDs will be applied prior to installing the chart, ensuring that Knative CRs can be created as part of the chart's deployment process. This approach eliminates the need to wait for the OpenShift Serverless Operator's subscription to install them beforehand. @@ -134,9 +144,3 @@ podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1b podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1beta1_knativeserving_crd.yaml > crds/knative-serving/knative-serving-crd.yaml ``` - -### OLM v1 API CRDs (chart-testing CI) - -KinD chart-testing with `olmVersion: v1` (see `ci/upstream-olm-v1-values.yaml`) requires the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so the chart's `ClusterExtension` resources can be admitted. CI downloads the pinned `operator-framework/operator-controller` release, keeps `CustomResourceDefinition` objects only, and applies them (`operator-controller` is not installed). - -Bump the OLM v1 API version used in chart-testing by updating `OPERATOR_CONTROLLER_VERSION` in the `Install OLM v1 API CRDs` step of [`.github/actions/test-charts/action.yml`](../../.github/actions/test-charts/action.yml). diff --git a/charts/orchestrator-infra/README.md.gotmpl b/charts/orchestrator-infra/README.md.gotmpl index d5e6f661..adc03797 100644 --- a/charts/orchestrator-infra/README.md.gotmpl +++ b/charts/orchestrator-infra/README.md.gotmpl @@ -88,6 +88,16 @@ helm install my-orchestrator-infra ./charts/orchestrator-infra --set olmVersion= helm install my-orchestrator-infra ./charts/orchestrator-infra --set olmVersion=v1 ``` +### OLM v1 API CRDs (chart-testing only) + +With `olmVersion: v1`, this chart creates `ClusterExtension` resources (see the previous section). Before Kubernetes can accept those objects, the cluster must already expose the OLM v1 API types `ClusterExtension` and `ClusterCatalog`. + +On OpenShift, those APIs usually come from the platform (you do not install them as part of this chart). The `rhdh-operator` plugin-infra flow assumes they are already present when it applies its own `ClusterExtension` manifests. + +Pull request CI tests this chart on a KinD cluster using `ci/upstream-olm-v1-values.yaml`. KinD does not ship OLM v1, so the shared test workflow downloads a pinned [operator-controller](https://github.com/operator-framework/operator-controller) release, applies only the `CustomResourceDefinition` manifests from that bundle, and does **not** install the operator-controller controller. That is enough for `helm template`, `ct install`, and `helm test` to validate the v1 chart output. + +To change which operator-controller release CI uses, update `OPERATOR_CONTROLLER_VERSION` in the `Install OLM v1 API CRDs (orchestrator-infra chart-testing)` step in [`.github/actions/test-charts/action.yml`](../../.github/actions/test-charts/action.yml). + ### Installing Knative Eventing and Knative Serving CRDs The orchestrator-infra chart requires several CRDs for Knative Eventing and Knative Serving. These CRDs will be applied prior to installing the chart, ensuring that Knative CRs can be created as part of the chart's deployment process. This approach eliminates the need to wait for the OpenShift Serverless Operator's subscription to install them beforehand. @@ -103,9 +113,3 @@ podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1b podman container run --rm --entrypoint cat "$osl_bundle" /manifests/operator_v1beta1_knativeserving_crd.yaml > crds/knative-serving/knative-serving-crd.yaml ``` - -### OLM v1 API CRDs (chart-testing CI) - -KinD chart-testing with `olmVersion: v1` (see `ci/upstream-olm-v1-values.yaml`) requires the OLM v1 API CRDs (`ClusterCatalog`, `ClusterExtension`) so the chart's `ClusterExtension` resources can be admitted. CI downloads the pinned `operator-framework/operator-controller` release, keeps `CustomResourceDefinition` objects only, and applies them (`operator-controller` is not installed). - -Bump the OLM v1 API version used in chart-testing by updating `OPERATOR_CONTROLLER_VERSION` in the `Install OLM v1 API CRDs` step of [`.github/actions/test-charts/action.yml`](../../.github/actions/test-charts/action.yml).