From e29724d32c46aee09d7aaeeefee598b5a1e00d68 Mon Sep 17 00:00:00 2001 From: Jordan Keister Date: Tue, 22 Oct 2024 14:11:15 -0500 Subject: [PATCH 1/3] updates from api audit Signed-off-by: Jordan Keister --- api/v1alpha1/clusterextension_types.go | 303 +++++++++--------- api/v1alpha1/zz_generated.deepcopy.go | 16 +- ...peratorframework.io_clusterextensions.yaml | 202 ++++++------ .../operator-controller-api-reference.md | 60 ++-- internal/applier/helm.go | 29 +- internal/conditionsets/conditionsets.go | 24 +- .../clusterextension_admission_test.go | 10 +- internal/resolve/catalog.go | 19 +- internal/resolve/catalog_test.go | 14 +- test/e2e/cluster_extension_install_test.go | 10 +- 10 files changed, 356 insertions(+), 331 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index 78608ba3a..35cdc6f92 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -18,15 +18,13 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/operator-framework/operator-controller/internal/conditionsets" ) var ClusterExtensionKind = "ClusterExtension" type ( - UpgradeConstraintPolicy string - CRDUpgradeSafetyPolicy string + UpgradeConstraintPolicy string + CRDUpgradeSafetyEnforcement string ) const ( @@ -58,6 +56,7 @@ type ClusterExtensionSpec struct { // catalog: // packageName: example-package // + // +kubebuilder:validation:Required Source SourceConfig `json:"source"` // install is a required field used to configure the installation options @@ -69,29 +68,34 @@ type ClusterExtensionSpec struct { // namespace: example-namespace // serviceAccount: // name: example-sa + // + // +kubebuilder:validation:Required Install ClusterExtensionInstallConfig `json:"install"` } const SourceTypeCatalog = "Catalog" // SourceConfig is a discriminated union which selects the installation source. +// // +union -// +kubebuilder:validation:XValidation:rule="self.sourceType == 'Catalog' && has(self.catalog)",message="sourceType Catalog requires catalog field" +// +kubebuilder:validation:XValidation:rule="has(self.sourceType) && self.sourceType == 'Catalog' ? has(self.catalog) : !has(self.catalog)",message="catalog is required when sourceType is Catalog, and forbidden otherwise" type SourceConfig struct { // sourceType is a required reference to the type of install source. // - // Allowed values are ["Catalog"] + // Allowed values are "Catalog" // - // When this field is set to "Catalog", information for determining the appropriate - // bundle of content to install will be fetched from ClusterCatalog resources existing - // on the cluster. When using the Catalog sourceType, the catalog field must also be set. + // When this field is set to "Catalog", information for determining the + // appropriate bundle of content to install will be fetched from + // ClusterCatalog resources existing on the cluster. + // When using the Catalog sourceType, the catalog field must also be set. // // +unionDiscriminator // +kubebuilder:validation:Enum:="Catalog" + // +kubebuilder:validation:Required SourceType string `json:"sourceType"` - // catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog", - // and must be the only field defined for this sourceType. + // catalog is used to configure how information is sourced from a catalog. + // This field is required when sourceType is "Catalog", and forbidden otherwise. // // +optional. Catalog *CatalogSource `json:"catalog,omitempty"` @@ -99,70 +103,58 @@ type SourceConfig struct { // ClusterExtensionInstallConfig is a union which selects the clusterExtension installation config. // ClusterExtensionInstallConfig requires the namespace and serviceAccount which should be used for the installation of packages. +// // +union type ClusterExtensionInstallConfig struct { - // namespace is a reference to the Namespace in which the bundle of - // content for the package referenced in the packageName field will be applied. + // namespace designates the kubernetes Namespace where bundle content + // for the 'packageName' package field will be applied and the necessary + // service account can be found. // The bundle may contain cluster-scoped resources or resources that are // applied to other Namespaces. This Namespace is expected to exist. // // namespace is required, immutable, and follows the DNS label standard - // as defined in [RFC 1123]. This means that valid values: - // - Contain no more than 63 characters - // - Contain only lowercase alphanumeric characters or '-' - // - Start with an alphanumeric character - // - End with an alphanumeric character - // - // Some examples of valid values are: - // - some-namespace - // - 123-namespace - // - 1-namespace-2 - // - somenamespace - // - // Some examples of invalid values are: - // - -some-namespace - // - some-namespace- - // - thisisareallylongnamespacenamethatisgreaterthanthemaximumlength - // - some.namespace + // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-), + // start and end with an alphanumeric character, and be no longer than 63 characters // // [RFC 1123]: https://tools.ietf.org/html/rfc1123 // - //+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - //+kubebuilder:validation:MaxLength:=63 - //+kubebuilder:validation:XValidation:rule="self == oldSelf",message="namespace is immutable" + // +kubebuilder:validation:MaxLength:=63 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="namespace is immutable" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$\")",message="namespace must be a valid DNS1123 label" + // +kubebuilder:validation:Required Namespace string `json:"namespace"` // serviceAccount is a required reference to a ServiceAccount that exists - // in the installNamespace. The provided ServiceAccount is used to install and + // in the installNamespace which is used to install and // manage the content for the package specified in the packageName field. // // In order to successfully install and manage the content for the package, // the ServiceAccount provided via this field should be configured with the // appropriate permissions to perform the necessary operations on all the // resources that are included in the bundle of content being applied. + // + // +kubebuilder:validation:Required ServiceAccount ServiceAccountReference `json:"serviceAccount"` - // preflight is an optional field that can be used to configure the preflight checks run before installation or upgrade of the content for the package specified in the packageName field. - // - // When specified, it overrides the default configuration of the preflight checks that are required to execute successfully during an install/upgrade operation. + // preflight is an optional field that can be used to configure the checks that are + // run before installation or upgrade of the content for the package specified in the packageName field. // - // When not specified, the default configuration for each preflight check will be used. + // When specified, it replaces the default preflight configuration for install/upgrade actions. + // When not specified, the default configuration will be used. // - //+optional + // +optional Preflight *PreflightConfig `json:"preflight,omitempty"` } -// CatalogSource defines the required fields for catalog source. +// CatalogSource defines the attributes used to identify and filter content from a catalog. type CatalogSource struct { // packageName is a reference to the name of the package to be installed // and is used to filter the content from catalogs. // - // This field is required, immutable and follows the DNS subdomain name - // standard as defined in [RFC 1123]. This means that valid entries: - // - Contain no more than 253 characters - // - Contain only lowercase alphanumeric characters, '-', or '.' - // - Start with an alphanumeric character - // - End with an alphanumeric character + // packageName is required, immutable, and follows the DNS subdomain standard + // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + // hyphens (-) or periods (.), start and end with an alphanumeric character, + // and be no longer than 253 characters. // // Some examples of valid values are: // - some-package @@ -178,9 +170,11 @@ type CatalogSource struct { // // [RFC 1123]: https://tools.ietf.org/html/rfc1123 // - //+kubebuilder:validation:MaxLength:=253 - //+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - //+kubebuilder:validation:XValidation:rule="self == oldSelf",message="packageName is immutable" + // +kubebuilder:validation.Required + // +kubebuilder:validation:MaxLength:=253 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="packageName is immutable" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="packageName must be a valid DNS1123 subdomain" + // +kubebuilder:validation:Required PackageName string `json:"packageName"` // version is an optional semver constraint (a specific version or range of versions). When unspecified, the latest version available will be installed. @@ -257,15 +251,20 @@ type CatalogSource struct { // // For more information on semver, please see https://semver.org/ // - //+kubebuilder:validation:MaxLength:=64 - //+kubebuilder:validation:Pattern=`^(\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\^)\s*(v?(0|[1-9]\d*|[x|X|\*])(\.(0|[1-9]\d*|x|X|\*]))?(\.(0|[1-9]\d*|x|X|\*))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)((?:\s+|,\s*|\s*\|\|\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\^)\s*(v?(0|[1-9]\d*|x|X|\*])(\.(0|[1-9]\d*|x|X|\*))?(\.(0|[1-9]\d*|x|X|\*]))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)*$` - //+optional + // +kubebuilder:validation:MaxLength:=64 + // +kubebuilder:validation:XValidation:rule="self.matches(\"^(\\\\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\\\\^)\\\\s*(v?(0|[1-9]\\\\d*|[x|X|\\\\*])(\\\\.(0|[1-9]\\\\d*|x|X|\\\\*]))?(\\\\.(0|[1-9]\\\\d*|x|X|\\\\*))?(-([0-9A-Za-z\\\\-]+(\\\\.[0-9A-Za-z\\\\-]+)*))?(\\\\+([0-9A-Za-z\\\\-]+(\\\\.[0-9A-Za-z\\\\-]+)*))?)\\\\s*)((?:\\\\s+|,\\\\s*|\\\\s*\\\\|\\\\|\\\\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\\\\^)\\\\s*(v?(0|[1-9]\\\\d*|x|X|\\\\*])(\\\\.(0|[1-9]\\\\d*|x|X|\\\\*))?(\\\\.(0|[1-9]\\\\d*|x|X|\\\\*]))?(-([0-9A-Za-z\\\\-]+(\\\\.[0-9A-Za-z\\\\-]+)*))?(\\\\+([0-9A-Za-z\\\\-]+(\\\\.[0-9A-Za-z\\\\-]+)*))?)\\\\s*)*$\")",message="invalid version expression" + // +optional Version string `json:"version,omitempty"` // channels is an optional reference to a set of channels belonging to // the package specified in the packageName field. // - // A "channel" is a package author defined stream of updates for an extension. + // A "channel" is a package-author-defined stream of updates for an extension. + // + // Each channel in the list must follow the DNS subdomain standard + // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + // hyphens (-) or periods (.), start and end with an alphanumeric character, + // and be no longer than 253 characters. // // When specified, it is used to constrain the set of installable bundles and // the automated upgrade path. This constraint is an AND operation with the @@ -277,13 +276,6 @@ type CatalogSource struct { // // When unspecified, upgrade edges across all channels will be used to identify valid automatic upgrade paths. // - // This field follows the DNS subdomain name standard as defined in [RFC - // 1123]. This means that valid entries: - // - Contain no more than 253 characters - // - Contain only lowercase alphanumeric characters, '-', or '.' - // - Start with an alphanumeric character - // - End with an alphanumeric character - // // Some examples of valid values are: // - 1.1.x // - alpha @@ -303,9 +295,10 @@ type CatalogSource struct { // // [RFC 1123]: https://tools.ietf.org/html/rfc1123 // - //+kubebuilder:validation:items:MaxLength:=253 - //+kubebuilder:validation:items:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - //+optional + // +kubebuilder:validation:items:MaxLength:=253 + // +kubebuilder:validation:MaxItems:=256 + // +kubebuilder:validation:items:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="channels entries must be valid DNS1123 subdomains" + // +optional Channels []string `json:"channels,omitempty"` // selector is an optional field that can be used @@ -315,14 +308,14 @@ type CatalogSource struct { // When unspecified, all ClusterCatalogs will be used in // the bundle selection process. // - //+optional - Selector metav1.LabelSelector `json:"selector,omitempty"` + // +optional + Selector *metav1.LabelSelector `json:"selector,omitempty"` // upgradeConstraintPolicy is an optional field that controls whether // the upgrade path(s) defined in the catalog are enforced for the package // referenced in the packageName field. // - // Allowed values are: ["CatalogProvided", "SelfCertified"]. + // Allowed values are: "CatalogProvided" or "SelfCertified", or omitted. // // When this field is set to "CatalogProvided", automatic upgrades will only occur // when upgrade constraints specified by the package author are met. @@ -334,28 +327,26 @@ type CatalogSource struct { // loss. It is assumed that users have independently verified changes when // using this option. // - // If unspecified, the default value is "CatalogProvided". + // When this field is omitted, the default value is "CatalogProvided". // - //+kubebuilder:validation:Enum:=CatalogProvided;SelfCertified - //+kubebuilder:default:=CatalogProvided - //+optional + // +kubebuilder:validation:Enum:=CatalogProvided;SelfCertified + // +kubebuilder:default:=CatalogProvided + // +optional UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"` } -// ServiceAccountReference references a serviceAccount. +// ServiceAccountReference identifies the serviceAccount used fo install a ClusterExtension. type ServiceAccountReference struct { // name is a required, immutable reference to the name of the ServiceAccount // to be used for installation and management of the content for the package // specified in the packageName field. // - // This ServiceAccount is expected to exist in the installNamespace. + // This ServiceAccount must exist in the installNamespace. // - // This field follows the DNS subdomain name standard as defined in [RFC - // 1123]. This means that valid values: - // - Contain no more than 253 characters - // - Contain only lowercase alphanumeric characters, '-', or '.' - // - Start with an alphanumeric character - // - End with an alphanumeric character + // name follows the DNS subdomain standard as defined in [RFC 1123]. + // It must contain only lowercase alphanumeric characters, + // hyphens (-) or periods (.), start and end with an alphanumeric character, + // and be no longer than 253 characters. // // Some examples of valid values are: // - some-serviceaccount @@ -370,13 +361,15 @@ type ServiceAccountReference struct { // // [RFC 1123]: https://tools.ietf.org/html/rfc1123 // - //+kubebuilder:validation:MaxLength:=253 - //+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - //+kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable" + // +kubebuilder:validation:MaxLength:=253 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="name must be a valid DNS1123 subdomain" + // +kubebuilder:validation:Required Name string `json:"name"` } // PreflightConfig holds the configuration for the preflight checks. If used, at least one preflight check must be non-nil. +// // +kubebuilder:validation:XValidation:rule="has(self.crdUpgradeSafety)",message="at least one of [crdUpgradeSafety] are required when preflight is specified" type PreflightConfig struct { // crdUpgradeSafety is used to configure the CRD Upgrade Safety pre-flight @@ -384,34 +377,29 @@ type PreflightConfig struct { // // The CRD Upgrade Safety pre-flight check safeguards from unintended // consequences of upgrading a CRD, such as data loss. - // - // This field is required if the spec.install.preflight field is specified. CRDUpgradeSafety *CRDUpgradeSafetyPreflightConfig `json:"crdUpgradeSafety"` } // CRDUpgradeSafetyPreflightConfig is the configuration for CRD upgrade safety preflight check. type CRDUpgradeSafetyPreflightConfig struct { - // policy is used to configure the state of the CRD Upgrade Safety pre-flight check. - // - // This field is required when the spec.install.preflight.crdUpgradeSafety field is - // specified. + // enforcement is a required field, used to configure the state of the CRD Upgrade Safety pre-flight check. // - // Allowed values are ["Enabled", "Disabled"]. The default value is "Enabled". + // Allowed values are "None" or "Strict". The default value is "Strict". // - // When set to "Disabled", the CRD Upgrade Safety pre-flight check will be skipped + // When set to "None", the CRD Upgrade Safety pre-flight check will be skipped // when performing an upgrade operation. This should be used with caution as // unintended consequences such as data loss can occur. // - // When set to "Enabled", the CRD Upgrade Safety pre-flight check will be run when + // When set to "Strict", the CRD Upgrade Safety pre-flight check will be run when // performing an upgrade operation. // - //+kubebuilder:validation:Enum:="Enabled";"Disabled" - //+kubebuilder:default:=Enabled - Policy CRDUpgradeSafetyPolicy `json:"policy"` + // +kubebuilder:validation:Enum:="None";"Strict" + // +kubebuilder:default:=Strict + // +kubebuilder:validation:Required + Enforcement CRDUpgradeSafetyEnforcement `json:"enforcement"` } const ( - // TODO(user): add more Types, here and into init() TypeInstalled = "Installed" TypeProgressing = "Progressing" @@ -428,110 +416,109 @@ const ( ReasonBlocked = "Blocked" ReasonRetrying = "Retrying" - CRDUpgradeSafetyPolicyEnabled CRDUpgradeSafetyPolicy = "Enabled" - CRDUpgradeSafetyPolicyDisabled CRDUpgradeSafetyPolicy = "Disabled" + // None will not perform CRD upgrade safety checks. + CRDUpgradeSafetyEnforcementNone CRDUpgradeSafetyEnforcement = "None" + // Strict will enforce the CRD upgrade safety check and block the upgrade if the CRD would not pass the check. + CRDUpgradeSafetyEnforcementStrict CRDUpgradeSafetyEnforcement = "Strict" ) -func init() { - // TODO(user): add Types from above - conditionsets.ConditionTypes = append(conditionsets.ConditionTypes, - TypeInstalled, - TypeDeprecated, - TypePackageDeprecated, - TypeChannelDeprecated, - TypeBundleDeprecated, - TypeProgressing, - ) - // TODO(user): add Reasons from above - conditionsets.ConditionReasons = append(conditionsets.ConditionReasons, - ReasonSucceeded, - ReasonDeprecated, - ReasonFailed, - ReasonBlocked, - ReasonRetrying, - ) -} - +// BundleMetadata is a representation of the identifying attributes of a bundle. type BundleMetadata struct { - // name is a required field and is a reference - // to the name of a bundle + // name is required and follows the DNS subdomain standard + // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + // hyphens (-) or periods (.), start and end with an alphanumeric character, + // and be no longer than 253 characters. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="packageName must be a valid DNS1123 subdomain" Name string `json:"name"` - // version is a required field and is a reference - // to the version that this bundle represents + + // version is a required field and is a reference to the version that this bundle represents + // version follows the semantic versioning standard as defined in https://semver.org/, but permits a leading 'v' character. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:XValidation:rule="self.matches(\"^v?([0-9]+)(\\\\.[0-9]+)?(\\\\.[0-9]+)?(-([-0-9A-Za-z]+(\\\\.[-0-9A-Za-z]+)*))?(\\\\+([-0-9A-Za-z]+(-\\\\.[-0-9A-Za-z]+)*))?\")",message="version name must foo" Version string `json:"version"` } -// ClusterExtensionStatus defines the observed state of ClusterExtension. +// ClusterExtensionStatus defines the observed state of a ClusterExtension. type ClusterExtensionStatus struct { - Install *ClusterExtensionInstallStatus `json:"install,omitempty"` - - // conditions is a representation of the current state for this ClusterExtension. - // The status is represented by a set of "conditions". - // - // Each condition is generally structured in the following format: - // - Type: a string representation of the condition type. More or less the condition "name". - // - Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"]. - // - Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations. - // - Message: a human readable message that further elaborates on the state of the condition - // - // The global set of condition types are: - // - "Installed", represents whether or not the a bundle has been installed for this ClusterExtension - // - "Progressing", represents whether or not the ClusterExtension is progressing towards a new state + // The set of condition types which apply to all spec.source variations are Installed and Progressing. // - // When the ClusterExtension is sourced from a catalog, the following conditions are also possible: - // - "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types - // - "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated - // - "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated - // - "BundleDeprecated", represents whether or not the installed bundle is deprecated + // The Installed condition represents whether or not the bundle has been installed for this ClusterExtension. + // When Installed is True and the Reason is Succeeded, the bundle has been successfully installed. + // When Installed is False and the Reason is Failed, the bundle has failed to install. // - // The current set of reasons are: - // - "Succeeded", this reason is set on the "Installed" and "Progressing" conditions when initial installation and progressing to a new state is successful - // - "Failed", this reason is set on the "Installed" condition when an error has occurred while performing the initial installation. - // - "Blocked", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that requires manual intervention for recovery - // - "Retrying", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that could be resolved on subsequent reconciliation attempts - // - "Deprecated", this reason is set on the "Deprecated", "PackageDeprecated", "ChannelDeprecated", and "BundleDeprecated" conditions to signal that the installed package has been deprecated at the particular scope + // The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. + // When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. + // When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. + // When Progressing is False and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. // + // When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition. + // These are indications from a package owner to guide users away from a particular package, channel, or bundle. + // BundleDeprecated is set if the requested bundle version is marked deprecated in the catalog. + // ChannelDeprecated is set if the requested channel is marked deprecated in the catalog. + // PackageDeprecated is set if the requested package is marked deprecated in the catalog. + // Deprecated is a rollup condition that is present when any of the deprecated conditions are present. // // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type + // +optional Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + + // install is a representation of the current installation status for this ClusterExtension. + // + // +optional + Install *ClusterExtensionInstallStatus `json:"install,omitempty"` } +// ClusterExtensionInstallStatus is a representation of the status of the identified bundle. type ClusterExtensionInstallStatus struct { - // bundle is a representation of the currently installed bundle. + // bundle is a required field which represents the identifying attributes of a bundle. // // A "bundle" is a versioned set of content that represents the resources that // need to be applied to a cluster to install a package. + // + // +kubebuilder:validation:Required Bundle BundleMetadata `json:"bundle"` } -//+kubebuilder:object:root=true -//+kubebuilder:resource:scope=Cluster -//+kubebuilder:subresource:status -//+kubebuilder:printcolumn:name="Installed Bundle",type=string,JSONPath=`.status.install.bundle.name` -//+kubebuilder:printcolumn:name=Version,type=string,JSONPath=`.status.install.bundle.version` -//+kubebuilder:printcolumn:name="Installed",type=string,JSONPath=`.status.conditions[?(@.type=='Installed')].status` -//+kubebuilder:printcolumn:name="Progressing",type=string,JSONPath=`.status.conditions[?(@.type=='Progressing')].status` -//+kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp` +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Installed Bundle",type=string,JSONPath=`.status.install.bundle.name` +// +kubebuilder:printcolumn:name=Version,type=string,JSONPath=`.status.install.bundle.version` +// +kubebuilder:printcolumn:name="Installed",type=string,JSONPath=`.status.conditions[?(@.type=='Installed')].status` +// +kubebuilder:printcolumn:name="Progressing",type=string,JSONPath=`.status.conditions[?(@.type=='Progressing')].status` +// +kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp` // ClusterExtension is the Schema for the clusterextensions API type ClusterExtension struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec ClusterExtensionSpec `json:"spec,omitempty"` + // spec is an optional field that defines the desired state of the ClusterExtension. + // +optional + Spec ClusterExtensionSpec `json:"spec,omitempty"` + + // status is an optional field that defines the observed state of the ClusterExtension. + // +optional Status ClusterExtensionStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // ClusterExtensionList contains a list of ClusterExtension type ClusterExtensionList struct { metav1.TypeMeta `json:",inline"` + + // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []ClusterExtension `json:"items"` + + // +kubebuilder:validation:Required + Items []ClusterExtension `json:"items"` } func init() { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ccd143aec..7ca0e7784 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -63,7 +63,11 @@ func (in *CatalogSource) DeepCopyInto(out *CatalogSource) { *out = make([]string, len(*in)) copy(*out, *in) } - in.Selector.DeepCopyInto(&out.Selector) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CatalogSource. @@ -192,11 +196,6 @@ func (in *ClusterExtensionSpec) DeepCopy() *ClusterExtensionSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) { *out = *in - if in.Install != nil { - in, out := &in.Install, &out.Install - *out = new(ClusterExtensionInstallStatus) - **out = **in - } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]v1.Condition, len(*in)) @@ -204,6 +203,11 @@ func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Install != nil { + in, out := &in.Install, &out.Install + *out = new(ClusterExtensionInstallStatus) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionStatus. diff --git a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml index 255010147..937bc9e8a 100644 --- a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml +++ b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -53,7 +53,8 @@ spec: metadata: type: object spec: - description: ClusterExtensionSpec defines the desired state of ClusterExtension + description: spec is an optional field that defines the desired state + of the ClusterExtension. properties: install: description: |- @@ -69,44 +70,31 @@ spec: properties: namespace: description: |- - namespace is a reference to the Namespace in which the bundle of - content for the package referenced in the packageName field will be applied. + namespace designates the kubernetes Namespace where bundle content + for the 'packageName' package field will be applied and the necessary + service account can be found. The bundle may contain cluster-scoped resources or resources that are applied to other Namespaces. This Namespace is expected to exist. namespace is required, immutable, and follows the DNS label standard - as defined in [RFC 1123]. This means that valid values: - - Contain no more than 63 characters - - Contain only lowercase alphanumeric characters or '-' - - Start with an alphanumeric character - - End with an alphanumeric character - - Some examples of valid values are: - - some-namespace - - 123-namespace - - 1-namespace-2 - - somenamespace - - Some examples of invalid values are: - - -some-namespace - - some-namespace- - - thisisareallylongnamespacenamethatisgreaterthanthemaximumlength - - some.namespace + as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-), + start and end with an alphanumeric character, and be no longer than 63 characters [RFC 1123]: https://tools.ietf.org/html/rfc1123 maxLength: 63 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string x-kubernetes-validations: - message: namespace is immutable rule: self == oldSelf + - message: namespace must be a valid DNS1123 label + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") preflight: description: |- - preflight is an optional field that can be used to configure the preflight checks run before installation or upgrade of the content for the package specified in the packageName field. + preflight is an optional field that can be used to configure the checks that are + run before installation or upgrade of the content for the package specified in the packageName field. - When specified, it overrides the default configuration of the preflight checks that are required to execute successfully during an install/upgrade operation. - - When not specified, the default configuration for each preflight check will be used. + When specified, it replaces the default preflight configuration for install/upgrade actions. + When not specified, the default configuration will be used. properties: crdUpgradeSafety: description: |- @@ -115,31 +103,26 @@ spec: The CRD Upgrade Safety pre-flight check safeguards from unintended consequences of upgrading a CRD, such as data loss. - - This field is required if the spec.install.preflight field is specified. properties: - policy: - default: Enabled + enforcement: + default: Strict description: |- - policy is used to configure the state of the CRD Upgrade Safety pre-flight check. + enforcement is a required field, used to configure the state of the CRD Upgrade Safety pre-flight check. - This field is required when the spec.install.preflight.crdUpgradeSafety field is - specified. + Allowed values are "None" or "Strict". The default value is "Strict". - Allowed values are ["Enabled", "Disabled"]. The default value is "Enabled". - - When set to "Disabled", the CRD Upgrade Safety pre-flight check will be skipped + When set to "None", the CRD Upgrade Safety pre-flight check will be skipped when performing an upgrade operation. This should be used with caution as unintended consequences such as data loss can occur. - When set to "Enabled", the CRD Upgrade Safety pre-flight check will be run when + When set to "Strict", the CRD Upgrade Safety pre-flight check will be run when performing an upgrade operation. enum: - - Enabled - - Disabled + - None + - Strict type: string required: - - policy + - enforcement type: object required: - crdUpgradeSafety @@ -151,7 +134,7 @@ spec: serviceAccount: description: |- serviceAccount is a required reference to a ServiceAccount that exists - in the installNamespace. The provided ServiceAccount is used to install and + in the installNamespace which is used to install and manage the content for the package specified in the packageName field. In order to successfully install and manage the content for the package, @@ -165,14 +148,12 @@ spec: to be used for installation and management of the content for the package specified in the packageName field. - This ServiceAccount is expected to exist in the installNamespace. + This ServiceAccount must exist in the installNamespace. - This field follows the DNS subdomain name standard as defined in [RFC - 1123]. This means that valid values: - - Contain no more than 253 characters - - Contain only lowercase alphanumeric characters, '-', or '.' - - Start with an alphanumeric character - - End with an alphanumeric character + name follows the DNS subdomain standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric character, + and be no longer than 253 characters. Some examples of valid values are: - some-serviceaccount @@ -187,11 +168,12 @@ spec: [RFC 1123]: https://tools.ietf.org/html/rfc1123 maxLength: 253 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string x-kubernetes-validations: - message: name is immutable rule: self == oldSelf + - message: name must be a valid DNS1123 subdomain + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") required: - name type: object @@ -216,15 +198,20 @@ spec: properties: catalog: description: |- - catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog", - and must be the only field defined for this sourceType. + catalog is used to configure how information is sourced from a catalog. + This field is required when sourceType is "Catalog", and forbidden otherwise. properties: channels: description: |- channels is an optional reference to a set of channels belonging to the package specified in the packageName field. - A "channel" is a package author defined stream of updates for an extension. + A "channel" is a package-author-defined stream of updates for an extension. + + Each channel in the list must follow the DNS subdomain standard + as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric character, + and be no longer than 253 characters. When specified, it is used to constrain the set of installable bundles and the automated upgrade path. This constraint is an AND operation with the @@ -236,13 +223,6 @@ spec: When unspecified, upgrade edges across all channels will be used to identify valid automatic upgrade paths. - This field follows the DNS subdomain name standard as defined in [RFC - 1123]. This means that valid entries: - - Contain no more than 253 characters - - Contain only lowercase alphanumeric characters, '-', or '.' - - Start with an alphanumeric character - - End with an alphanumeric character - Some examples of valid values are: - 1.1.x - alpha @@ -263,20 +243,21 @@ spec: [RFC 1123]: https://tools.ietf.org/html/rfc1123 items: maxLength: 253 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string + x-kubernetes-validations: + - message: channels entries must be valid DNS1123 subdomains + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + maxItems: 256 type: array packageName: description: |- packageName is a reference to the name of the package to be installed and is used to filter the content from catalogs. - This field is required, immutable and follows the DNS subdomain name - standard as defined in [RFC 1123]. This means that valid entries: - - Contain no more than 253 characters - - Contain only lowercase alphanumeric characters, '-', or '.' - - Start with an alphanumeric character - - End with an alphanumeric character + packageName is required, immutable, and follows the DNS subdomain standard + as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric character, + and be no longer than 253 characters. Some examples of valid values are: - some-package @@ -292,11 +273,12 @@ spec: [RFC 1123]: https://tools.ietf.org/html/rfc1123 maxLength: 253 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string x-kubernetes-validations: - message: packageName is immutable rule: self == oldSelf + - message: packageName must be a valid DNS1123 subdomain + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") selector: description: |- selector is an optional field that can be used @@ -356,7 +338,7 @@ spec: the upgrade path(s) defined in the catalog are enforced for the package referenced in the packageName field. - Allowed values are: ["CatalogProvided", "SelfCertified"]. + Allowed values are: "CatalogProvided" or "SelfCertified", or omitted. When this field is set to "CatalogProvided", automatic upgrades will only occur when upgrade constraints specified by the package author are met. @@ -368,7 +350,7 @@ spec: loss. It is assumed that users have independently verified changes when using this option. - If unspecified, the default value is "CatalogProvided". + When this field is omitted, the default value is "CatalogProvided". enum: - CatalogProvided - SelfCertified @@ -449,8 +431,10 @@ spec: For more information on semver, please see https://semver.org/ maxLength: 64 - pattern: ^(\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\^)\s*(v?(0|[1-9]\d*|[x|X|\*])(\.(0|[1-9]\d*|x|X|\*]))?(\.(0|[1-9]\d*|x|X|\*))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)((?:\s+|,\s*|\s*\|\|\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\^)\s*(v?(0|[1-9]\d*|x|X|\*])(\.(0|[1-9]\d*|x|X|\*))?(\.(0|[1-9]\d*|x|X|\*]))?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?)\s*)*$ type: string + x-kubernetes-validations: + - message: invalid version expression + rule: self.matches("^(\\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|[x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*]))?(\\.(0|[1-9]\\d*|x|X|\\*))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)((?:\\s+|,\\s*|\\s*\\|\\|\\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*))?(\\.(0|[1-9]\\d*|x|X|\\*]))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)*$") required: - packageName type: object @@ -458,11 +442,12 @@ spec: description: |- sourceType is a required reference to the type of install source. - Allowed values are ["Catalog"] + Allowed values are "Catalog" - When this field is set to "Catalog", information for determining the appropriate - bundle of content to install will be fetched from ClusterCatalog resources existing - on the cluster. When using the Catalog sourceType, the catalog field must also be set. + When this field is set to "Catalog", information for determining the + appropriate bundle of content to install will be fetched from + ClusterCatalog resources existing on the cluster. + When using the Catalog sourceType, the catalog field must also be set. enum: - Catalog type: string @@ -470,42 +455,37 @@ spec: - sourceType type: object x-kubernetes-validations: - - message: sourceType Catalog requires catalog field - rule: self.sourceType == 'Catalog' && has(self.catalog) + - message: catalog is required when sourceType is Catalog, and forbidden + otherwise + rule: 'has(self.sourceType) && self.sourceType == ''Catalog'' ? + has(self.catalog) : !has(self.catalog)' required: - install - source type: object status: - description: ClusterExtensionStatus defines the observed state of ClusterExtension. + description: status is an optional field that defines the observed state + of the ClusterExtension. properties: conditions: description: |- - conditions is a representation of the current state for this ClusterExtension. - The status is represented by a set of "conditions". - - Each condition is generally structured in the following format: - - Type: a string representation of the condition type. More or less the condition "name". - - Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"]. - - Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations. - - Message: a human readable message that further elaborates on the state of the condition - - The global set of condition types are: - - "Installed", represents whether or not the a bundle has been installed for this ClusterExtension - - "Progressing", represents whether or not the ClusterExtension is progressing towards a new state - - When the ClusterExtension is sourced from a catalog, the following conditions are also possible: - - "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types - - "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated - - "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated - - "BundleDeprecated", represents whether or not the installed bundle is deprecated - - The current set of reasons are: - - "Succeeded", this reason is set on the "Installed" and "Progressing" conditions when initial installation and progressing to a new state is successful - - "Failed", this reason is set on the "Installed" condition when an error has occurred while performing the initial installation. - - "Blocked", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that requires manual intervention for recovery - - "Retrying", this reason is set on the "Progressing" condition when the ClusterExtension controller has encountered an error that could be resolved on subsequent reconciliation attempts - - "Deprecated", this reason is set on the "Deprecated", "PackageDeprecated", "ChannelDeprecated", and "BundleDeprecated" conditions to signal that the installed package has been deprecated at the particular scope + The set of condition types which apply to all spec.source variations are Installed and Progressing. + + The Installed condition represents whether or not the bundle has been installed for this ClusterExtension. + When Installed is True and the Reason is Succeeded, the bundle has been successfully installed. + When Installed is False and the Reason is Failed, the bundle has failed to install. + + The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. + When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. + When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. + When Progressing is False and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. + + When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition. + These are indications from a package owner to guide users away from a particular package, channel, or bundle. + BundleDeprecated is set if the requested bundle version is marked deprecated in the catalog. + ChannelDeprecated is set if the requested channel is marked deprecated in the catalog. + PackageDeprecated is set if the requested package is marked deprecated in the catalog. + Deprecated is a rollup condition that is present when any of the deprecated conditions are present. items: description: Condition contains details for one aspect of the current state of this API Resource. @@ -565,24 +545,34 @@ spec: - type x-kubernetes-list-type: map install: + description: install is a representation of the current installation + status for this ClusterExtension. properties: bundle: description: |- - bundle is a representation of the currently installed bundle. + bundle is a required field which represents the identifying attributes of a bundle. A "bundle" is a versioned set of content that represents the resources that need to be applied to a cluster to install a package. properties: name: description: |- - name is a required field and is a reference - to the name of a bundle + name is required and follows the DNS subdomain standard + as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric character, + and be no longer than 253 characters. type: string + x-kubernetes-validations: + - message: packageName must be a valid DNS1123 subdomain + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") version: description: |- - version is a required field and is a reference - to the version that this bundle represents + version is a required field and is a reference to the version that this bundle represents + version follows the semantic versioning standard as defined in https://semver.org/, but permits a leading 'v' character. type: string + x-kubernetes-validations: + - message: version name must foo + rule: self.matches("^v?([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-([-0-9A-Za-z]+(\\.[-0-9A-Za-z]+)*))?(\\+([-0-9A-Za-z]+(-\\.[-0-9A-Za-z]+)*))?") required: - name - version diff --git a/docs/api-reference/operator-controller-api-reference.md b/docs/api-reference/operator-controller-api-reference.md index 86bd90190..02d730ace 100644 --- a/docs/api-reference/operator-controller-api-reference.md +++ b/docs/api-reference/operator-controller-api-reference.md @@ -18,7 +18,7 @@ Package v1alpha1 contains API Schema definitions for the olm v1alpha1 API group - +BundleMetadata is a representation of the identifying attributes of a bundle. @@ -27,11 +27,11 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `name` _string_ | name is a required field and is a reference
to the name of a bundle | | | -| `version` _string_ | version is a required field and is a reference
to the version that this bundle represents | | | +| `name` _string_ | name is required and follows the DNS subdomain standard
as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters,
hyphens (-) or periods (.), start and end with an alphanumeric character,
and be no longer than 253 characters. | | Required: \{\}
| +| `version` _string_ | version is a required field and is a reference to the version that this bundle represents
version follows the semantic versioning standard as defined in https://semver.org/, but permits a leading 'v' character. | | Required: \{\}
| -#### CRDUpgradeSafetyPolicy +#### CRDUpgradeSafetyEnforcement _Underlying type:_ _string_ @@ -44,8 +44,8 @@ _Appears in:_ | Field | Description | | --- | --- | -| `Enabled` | | -| `Disabled` | | +| `None` | None will not perform CRD upgrade safety checks.
| +| `Strict` | Strict will enforce the CRD upgrade safety check and block the upgrade if the CRD would not pass the check.
| #### CRDUpgradeSafetyPreflightConfig @@ -61,14 +61,14 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `policy` _[CRDUpgradeSafetyPolicy](#crdupgradesafetypolicy)_ | policy is used to configure the state of the CRD Upgrade Safety pre-flight check.

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

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

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

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

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

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

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

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

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

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

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

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

# Range Comparisons

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

# Pinned Versions

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

# Basic Comparison Operators

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

# Wildcard Comparisons

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

# Patch Release Comparisons

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

# Major Release Comparisons

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# Range Comparisons

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

# Pinned Versions

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

# Basic Comparison Operators

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

# Wildcard Comparisons

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

# Patch Release Comparisons

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

# Major Release Comparisons

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

When this field is omitted, the default value is "CatalogProvided". | CatalogProvided | Enum: [CatalogProvided SelfCertified]
| #### ClusterExtension @@ -102,8 +102,8 @@ _Appears in:_ | `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | | | `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | | | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `spec` _[ClusterExtensionSpec](#clusterextensionspec)_ | | | | -| `status` _[ClusterExtensionStatus](#clusterextensionstatus)_ | | | | +| `spec` _[ClusterExtensionSpec](#clusterextensionspec)_ | spec is an optional field that defines the desired state of the ClusterExtension. | | | +| `status` _[ClusterExtensionStatus](#clusterextensionstatus)_ | status is an optional field that defines the observed state of the ClusterExtension. | | | #### ClusterExtensionInstallConfig @@ -120,16 +120,16 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `namespace` _string_ | namespace is a reference to the Namespace in which the bundle of
content for the package referenced in the packageName field will be applied.
The bundle may contain cluster-scoped resources or resources that are
applied to other Namespaces. This Namespace is expected to exist.

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

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

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

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

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

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

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

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

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

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

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

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

A "bundle" is a versioned set of content that represents the resources that
need to be applied to a cluster to install a package. | | Required: \{\}
| #### ClusterExtensionList @@ -158,7 +158,7 @@ ClusterExtensionList contains a list of ClusterExtension | `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | | | `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | | | `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `items` _[ClusterExtension](#clusterextension) array_ | | | | +| `items` _[ClusterExtension](#clusterextension) array_ | | | Required: \{\}
| #### ClusterExtensionSpec @@ -174,15 +174,15 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `source` _[SourceConfig](#sourceconfig)_ | source is a required field which selects the installation source of content
for this ClusterExtension. Selection is performed by setting the sourceType.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This ServiceAccount is expected to exist in the installNamespace.

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

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

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

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

This ServiceAccount must exist in the installNamespace.

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

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

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

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

Allowed values are ["Catalog"]

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

Allowed values are "Catalog"

When this field is set to "Catalog", information for determining the
appropriate bundle of content to install will be fetched from
ClusterCatalog resources existing on the cluster.
When using the Catalog sourceType, the catalog field must also be set. | | Enum: [Catalog]
Required: \{\}
| +| `catalog` _[CatalogSource](#catalogsource)_ | catalog is used to configure how information is sourced from a catalog.
This field is required when sourceType is "Catalog", and forbidden otherwise. | | | #### UpgradeConstraintPolicy diff --git a/internal/applier/helm.go b/internal/applier/helm.go index ac43726f1..97b75ed36 100644 --- a/internal/applier/helm.go +++ b/internal/applier/helm.go @@ -19,6 +19,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" apimachyaml "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" @@ -56,6 +57,26 @@ type Helm struct { Preflights []Preflight } +// shouldSkipPreflight is a helper to determine if the preflight check is CRDUpgradeSafety AND +// if it is set to enforcement None. +func shouldSkipPreflight(preflight Preflight, ctx context.Context, ext *ocv1alpha1.ClusterExtension, state string) bool { + l := log.FromContext(ctx) + hasCRDUpgradeSafety := ext.Spec.Install.Preflight != nil && ext.Spec.Install.Preflight.CRDUpgradeSafety != nil + _, isCRDUpgradeSafetyInstance := preflight.(*crdupgradesafety.Preflight) + + if hasCRDUpgradeSafety && isCRDUpgradeSafetyInstance { + if state == StateNeedsInstall || state == StateNeedsUpgrade { + l.Info("crdUpgradeSafety ", "policy", ext.Spec.Install.Preflight.CRDUpgradeSafety.Enforcement) + } + if ext.Spec.Install.Preflight.CRDUpgradeSafety.Enforcement == ocv1alpha1.CRDUpgradeSafetyEnforcementNone { + // Skip this preflight check because it is of type *crdupgradesafety.Preflight and the CRD Upgrade Safety + // policy is set to None + return true + } + } + return false +} + func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1alpha1.ClusterExtension, objectLabels map[string]string, storageLabels map[string]string) ([]client.Object, string, error) { chrt, err := convert.RegistryV1ToHelmChart(ctx, contentFS, ext.Spec.Install.Namespace, []string{corev1.NamespaceAll}) if err != nil { @@ -78,12 +99,8 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1alpha1.Clust } for _, preflight := range h.Preflights { - if ext.Spec.Install.Preflight != nil && ext.Spec.Install.Preflight.CRDUpgradeSafety != nil { - if _, ok := preflight.(*crdupgradesafety.Preflight); ok && ext.Spec.Install.Preflight.CRDUpgradeSafety.Policy == ocv1alpha1.CRDUpgradeSafetyPolicyDisabled { - // Skip this preflight check because it is of type *crdupgradesafety.Preflight and the CRD Upgrade Safety - // preflight check has been disabled - continue - } + if shouldSkipPreflight(preflight, ctx, ext, state) { + continue } switch state { case StateNeedsInstall: diff --git a/internal/conditionsets/conditionsets.go b/internal/conditionsets/conditionsets.go index fa4087148..253e0d5eb 100644 --- a/internal/conditionsets/conditionsets.go +++ b/internal/conditionsets/conditionsets.go @@ -16,9 +16,27 @@ limitations under the License. package conditionsets +import ( + "github.com/operator-framework/operator-controller/api/v1alpha1" +) + // ConditionTypes is the full set of ClusterExtension condition Types. // ConditionReasons is the full set of ClusterExtension condition Reasons. // -// NOTE: These are populated by init() in api/v1alpha1/clusterextension_types.go -var ConditionTypes []string -var ConditionReasons []string +// NOTE: unit tests in clusterextension_types_test will enforce completeness. +var ConditionTypes = []string{ + v1alpha1.TypeInstalled, + v1alpha1.TypeDeprecated, + v1alpha1.TypePackageDeprecated, + v1alpha1.TypeChannelDeprecated, + v1alpha1.TypeBundleDeprecated, + v1alpha1.TypeProgressing, +} + +var ConditionReasons = []string{ + v1alpha1.ReasonSucceeded, + v1alpha1.ReasonDeprecated, + v1alpha1.ReasonFailed, + v1alpha1.ReasonBlocked, + v1alpha1.ReasonRetrying, +} diff --git a/internal/controllers/clusterextension_admission_test.go b/internal/controllers/clusterextension_admission_test.go index c7bc3ae1a..cc3ac9b2d 100644 --- a/internal/controllers/clusterextension_admission_test.go +++ b/internal/controllers/clusterextension_admission_test.go @@ -77,7 +77,7 @@ func TestClusterExtensionSourceConfig(t *testing.T) { func TestClusterExtensionAdmissionPackageName(t *testing.T) { tooLongError := "spec.source.catalog.packageName: Too long: may not be longer than 253" - regexMismatchError := "spec.source.catalog.packageName in body should match" + regexMismatchError := "packageName must be a valid DNS1123 subdomain" testCases := []struct { name string @@ -136,7 +136,7 @@ func TestClusterExtensionAdmissionPackageName(t *testing.T) { func TestClusterExtensionAdmissionVersion(t *testing.T) { tooLongError := "spec.source.catalog.version: Too long: may not be longer than 64" - regexMismatchError := "spec.source.catalog.version in body should match" + regexMismatchError := "invalid version expression" testCases := []struct { name string @@ -236,7 +236,7 @@ func TestClusterExtensionAdmissionVersion(t *testing.T) { func TestClusterExtensionAdmissionChannel(t *testing.T) { tooLongError := "spec.source.catalog.channels[0]: Too long: may not be longer than 253" - regexMismatchError := "spec.source.catalog.channels[0] in body should match" + regexMismatchError := "channels entries must be valid DNS1123 subdomains" testCases := []struct { name string @@ -293,7 +293,7 @@ func TestClusterExtensionAdmissionChannel(t *testing.T) { func TestClusterExtensionAdmissionInstallNamespace(t *testing.T) { tooLongError := "spec.install.namespace: Too long: may not be longer than 63" - regexMismatchError := "spec.install.namespace in body should match" + regexMismatchError := "namespace must be a valid DNS1123 label" testCases := []struct { name string @@ -348,7 +348,7 @@ func TestClusterExtensionAdmissionInstallNamespace(t *testing.T) { func TestClusterExtensionAdmissionServiceAccount(t *testing.T) { tooLongError := "spec.install.serviceAccount.name: Too long: may not be longer than 253" - regexMismatchError := "spec.install.serviceAccount.name in body should match" + regexMismatchError := "name must be a valid DNS1123 subdomain" testCases := []struct { name string diff --git a/internal/resolve/catalog.go b/internal/resolve/catalog.go index 78aa7b4ae..761448bea 100644 --- a/internal/resolve/catalog.go +++ b/internal/resolve/catalog.go @@ -43,13 +43,18 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1alpha1.ClusterEx versionRange := ext.Spec.Source.Catalog.Version channels := ext.Spec.Source.Catalog.Channels - selector, err := metav1.LabelSelectorAsSelector(&ext.Spec.Source.Catalog.Selector) - if err != nil { - return nil, nil, nil, fmt.Errorf("desired catalog selector is invalid: %w", err) - } - // A nothing (empty) seletor selects everything - if selector == labels.Nothing() { - selector = labels.Everything() + // unless overridden, default to selecting all bundles + var selector = labels.Everything() + var err error + if ext.Spec.Source.Catalog != nil { + selector, err = metav1.LabelSelectorAsSelector(ext.Spec.Source.Catalog.Selector) + if err != nil { + return nil, nil, nil, fmt.Errorf("desired catalog selector is invalid: %w", err) + } + // A nothing (empty) selector selects everything + if selector == labels.Nothing() { + selector = labels.Everything() + } } var versionRangeConstraints *mmsemver.Constraints diff --git a/internal/resolve/catalog_test.go b/internal/resolve/catalog_test.go index 6e69c03cc..755c767d2 100644 --- a/internal/resolve/catalog_test.go +++ b/internal/resolve/catalog_test.go @@ -770,7 +770,7 @@ func TestInvalidClusterExtensionCatalogMatchExpressions(t *testing.T) { Source: ocv1alpha1.SourceConfig{ Catalog: &ocv1alpha1.CatalogSource{ PackageName: "foo", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { Key: "name", @@ -802,7 +802,7 @@ func TestInvalidClusterExtensionCatalogMatchLabelsName(t *testing.T) { Source: ocv1alpha1.SourceConfig{ Catalog: &ocv1alpha1.CatalogSource{ PackageName: "foo", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"": "value"}, }, }, @@ -828,7 +828,7 @@ func TestInvalidClusterExtensionCatalogMatchLabelsValue(t *testing.T) { Source: ocv1alpha1.SourceConfig{ Catalog: &ocv1alpha1.CatalogSource{ PackageName: "foo", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"name": "&value"}, }, }, @@ -852,7 +852,9 @@ func TestClusterExtensionMatchLabel(t *testing.T) { } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) - ce.Spec.Source.Catalog.Selector.MatchLabels = map[string]string{"olm.operatorframework.io/metadata.name": "b"} + ce.Spec.Source.Catalog.Selector = &metav1.LabelSelector{ + MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": "b"}, + } _, _, _, err := r.Resolve(context.Background(), ce, nil) require.NoError(t, err) @@ -871,7 +873,9 @@ func TestClusterExtensionNoMatchLabel(t *testing.T) { } r := CatalogResolver{WalkCatalogsFunc: w.WalkCatalogs} ce := buildFooClusterExtension(pkgName, []string{}, "", ocv1alpha1.UpgradeConstraintPolicyCatalogProvided) - ce.Spec.Source.Catalog.Selector.MatchLabels = map[string]string{"olm.operatorframework.io/metadata.name": "a"} + ce.Spec.Source.Catalog.Selector = &metav1.LabelSelector{ + MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": "a"}, + } _, _, _, err := r.Resolve(context.Background(), ce, nil) require.Error(t, err) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index c141ff0ca..884b46d1d 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -313,7 +313,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: tc.packageName, - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, }, @@ -580,7 +580,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: "prometheus", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { Key: "olm.operatorframework.io/metadata.name", @@ -669,7 +669,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: "prometheus", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, }, @@ -732,7 +732,7 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: "prometheus", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, }, @@ -797,7 +797,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes SourceType: "Catalog", Catalog: &ocv1alpha1.CatalogSource{ PackageName: "prometheus", - Selector: metav1.LabelSelector{ + Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, }, From 81890154ce44716ffcb6022f2dd78d0a503b6496 Mon Sep 17 00:00:00 2001 From: Jordan Keister Date: Fri, 8 Nov 2024 08:21:03 -0600 Subject: [PATCH 2/3] review updates Signed-off-by: Jordan Keister --- api/v1alpha1/clusterextension_types.go | 23 ++++++------- ...peratorframework.io_clusterextensions.yaml | 33 ++++++++++++------- .../operator-controller-api-reference.md | 12 +++---- internal/applier/helm.go | 4 +-- .../clusterextension_controller_test.go | 2 +- internal/controllers/common_controller.go | 3 +- .../controllers/common_controller_test.go | 4 +-- 7 files changed, 46 insertions(+), 35 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index 35cdc6f92..c0fba0098 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -97,7 +97,7 @@ type SourceConfig struct { // catalog is used to configure how information is sourced from a catalog. // This field is required when sourceType is "Catalog", and forbidden otherwise. // - // +optional. + // +optional Catalog *CatalogSource `json:"catalog,omitempty"` } @@ -107,7 +107,7 @@ type SourceConfig struct { // +union type ClusterExtensionInstallConfig struct { // namespace designates the kubernetes Namespace where bundle content - // for the 'packageName' package field will be applied and the necessary + // for the package, referenced in the 'packageName' field, will be applied and the necessary // service account can be found. // The bundle may contain cluster-scoped resources or resources that are // applied to other Namespaces. This Namespace is expected to exist. @@ -120,7 +120,7 @@ type ClusterExtensionInstallConfig struct { // // +kubebuilder:validation:MaxLength:=63 // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="namespace is immutable" - // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$\")",message="namespace must be a valid DNS1123 label" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$\")",message="namespace must be a valid DNS1123 label. It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character, and be no longer than 63 characters" // +kubebuilder:validation:Required Namespace string `json:"namespace"` @@ -173,7 +173,7 @@ type CatalogSource struct { // +kubebuilder:validation.Required // +kubebuilder:validation:MaxLength:=253 // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="packageName is immutable" - // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="packageName must be a valid DNS1123 subdomain" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="packageName must be a valid DNS1123 subdomain. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters" // +kubebuilder:validation:Required PackageName string `json:"packageName"` @@ -264,7 +264,7 @@ type CatalogSource struct { // Each channel in the list must follow the DNS subdomain standard // as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, // hyphens (-) or periods (.), start and end with an alphanumeric character, - // and be no longer than 253 characters. + // and be no longer than 253 characters. No more than 256 channels can be specified. // // When specified, it is used to constrain the set of installable bundles and // the automated upgrade path. This constraint is an AND operation with the @@ -363,7 +363,7 @@ type ServiceAccountReference struct { // // +kubebuilder:validation:MaxLength:=253 // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable" - // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="name must be a valid DNS1123 subdomain" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="name must be a valid DNS1123 subdomain. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters" // +kubebuilder:validation:Required Name string `json:"name"` } @@ -394,7 +394,6 @@ type CRDUpgradeSafetyPreflightConfig struct { // performing an upgrade operation. // // +kubebuilder:validation:Enum:="None";"Strict" - // +kubebuilder:default:=Strict // +kubebuilder:validation:Required Enforcement CRDUpgradeSafetyEnforcement `json:"enforcement"` } @@ -430,14 +429,14 @@ type BundleMetadata struct { // and be no longer than 253 characters. // // +kubebuilder:validation:Required - // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="packageName must be a valid DNS1123 subdomain" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="packageName must be a valid DNS1123 subdomain. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters" Name string `json:"name"` // version is a required field and is a reference to the version that this bundle represents - // version follows the semantic versioning standard as defined in https://semver.org/, but permits a leading 'v' character. + // version follows the semantic versioning standard as defined in https://semver.org/. // // +kubebuilder:validation:Required - // +kubebuilder:validation:XValidation:rule="self.matches(\"^v?([0-9]+)(\\\\.[0-9]+)?(\\\\.[0-9]+)?(-([-0-9A-Za-z]+(\\\\.[-0-9A-Za-z]+)*))?(\\\\+([-0-9A-Za-z]+(-\\\\.[-0-9A-Za-z]+)*))?\")",message="version name must foo" + // +kubebuilder:validation:XValidation:rule="self.matches(\"^([0-9]+)(\\\\.[0-9]+)?(\\\\.[0-9]+)?(-([-0-9A-Za-z]+(\\\\.[-0-9A-Za-z]+)*))?(\\\\+([-0-9A-Za-z]+(-\\\\.[-0-9A-Za-z]+)*))?\")",message="version must be well-formed semver" Version string `json:"version"` } @@ -451,8 +450,8 @@ type ClusterExtensionStatus struct { // // The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. // When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. + // When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. // When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. - // When Progressing is False and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. // // When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition. // These are indications from a package owner to guide users away from a particular package, channel, or bundle. @@ -517,6 +516,8 @@ type ClusterExtensionList struct { // +optional metav1.ListMeta `json:"metadata,omitempty"` + // items is a required list of ClusterExtension objects. + // // +kubebuilder:validation:Required Items []ClusterExtension `json:"items"` } diff --git a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml index 937bc9e8a..106605424 100644 --- a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml +++ b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -71,7 +71,7 @@ spec: namespace: description: |- namespace designates the kubernetes Namespace where bundle content - for the 'packageName' package field will be applied and the necessary + for the package, referenced in the 'packageName' field, will be applied and the necessary service account can be found. The bundle may contain cluster-scoped resources or resources that are applied to other Namespaces. This Namespace is expected to exist. @@ -86,7 +86,10 @@ spec: x-kubernetes-validations: - message: namespace is immutable rule: self == oldSelf - - message: namespace must be a valid DNS1123 label + - message: namespace must be a valid DNS1123 label. It must contain + only lowercase alphanumeric characters or hyphens (-), start + and end with an alphanumeric character, and be no longer than + 63 characters rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") preflight: description: |- @@ -105,7 +108,6 @@ spec: consequences of upgrading a CRD, such as data loss. properties: enforcement: - default: Strict description: |- enforcement is a required field, used to configure the state of the CRD Upgrade Safety pre-flight check. @@ -172,7 +174,10 @@ spec: x-kubernetes-validations: - message: name is immutable rule: self == oldSelf - - message: name must be a valid DNS1123 subdomain + - message: name must be a valid DNS1123 subdomain. It must + contain only lowercase alphanumeric characters, hyphens + (-) or periods (.), start and end with an alphanumeric + character, and be no longer than 253 characters rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") required: - name @@ -211,7 +216,7 @@ spec: Each channel in the list must follow the DNS subdomain standard as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, - and be no longer than 253 characters. + and be no longer than 253 characters. No more than 256 channels can be specified. When specified, it is used to constrain the set of installable bundles and the automated upgrade path. This constraint is an AND operation with the @@ -277,7 +282,10 @@ spec: x-kubernetes-validations: - message: packageName is immutable rule: self == oldSelf - - message: packageName must be a valid DNS1123 subdomain + - 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: |- @@ -477,8 +485,8 @@ spec: The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. + When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. - When Progressing is False and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition. These are indications from a package owner to guide users away from a particular package, channel, or bundle. @@ -563,16 +571,19 @@ spec: and be no longer than 253 characters. type: string x-kubernetes-validations: - - message: packageName must be a valid DNS1123 subdomain + - message: packageName must be a valid DNS1123 subdomain. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric + character, and be no longer than 253 characters rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") version: description: |- version is a required field and is a reference to the version that this bundle represents - version follows the semantic versioning standard as defined in https://semver.org/, but permits a leading 'v' character. + version follows the semantic versioning standard as defined in https://semver.org/. type: string x-kubernetes-validations: - - message: version name must foo - rule: self.matches("^v?([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-([-0-9A-Za-z]+(\\.[-0-9A-Za-z]+)*))?(\\+([-0-9A-Za-z]+(-\\.[-0-9A-Za-z]+)*))?") + - message: version must be well-formed semver + rule: self.matches("^([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-([-0-9A-Za-z]+(\\.[-0-9A-Za-z]+)*))?(\\+([-0-9A-Za-z]+(-\\.[-0-9A-Za-z]+)*))?") required: - name - version diff --git a/docs/api-reference/operator-controller-api-reference.md b/docs/api-reference/operator-controller-api-reference.md index 02d730ace..f6553a7a4 100644 --- a/docs/api-reference/operator-controller-api-reference.md +++ b/docs/api-reference/operator-controller-api-reference.md @@ -28,7 +28,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `name` _string_ | name is required and follows the DNS subdomain standard
as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters,
hyphens (-) or periods (.), start and end with an alphanumeric character,
and be no longer than 253 characters. | | Required: \{\}
| -| `version` _string_ | version is a required field and is a reference to the version that this bundle represents
version follows the semantic versioning standard as defined in https://semver.org/, but permits a leading 'v' character. | | Required: \{\}
| +| `version` _string_ | version is a required field and is a reference to the version that this bundle represents
version follows the semantic versioning standard as defined in https://semver.org/. | | Required: \{\}
| #### CRDUpgradeSafetyEnforcement @@ -61,7 +61,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `enforcement` _[CRDUpgradeSafetyEnforcement](#crdupgradesafetyenforcement)_ | enforcement is a required field, used to configure the state of the CRD Upgrade Safety pre-flight check.

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

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

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

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

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

When set to "Strict", the CRD Upgrade Safety pre-flight check will be run when
performing an upgrade operation. | | Enum: [None Strict]
Required: \{\}
| #### CatalogSource @@ -79,7 +79,7 @@ _Appears in:_ | --- | --- | --- | --- | | `packageName` _string_ | packageName is a reference to the name of the package to be installed
and is used to filter the content from catalogs.

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

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

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

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

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

# Range Comparisons

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

# Pinned Versions

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

# Basic Comparison Operators

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

# Wildcard Comparisons

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

# Patch Release Comparisons

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

# Major Release Comparisons

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

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

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

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

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

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

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

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

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

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxItems: 256
| +| `channels` _string array_ | channels is an optional reference to a set of channels belonging to
the package specified in the packageName field.

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

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

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

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

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

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

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

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

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

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

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

When this field is omitted, the default value is "CatalogProvided". | CatalogProvided | Enum: [CatalogProvided SelfCertified]
| @@ -120,7 +120,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `namespace` _string_ | namespace designates the kubernetes Namespace where bundle content
for the 'packageName' package field will be applied and the necessary
service account can be found.
The bundle may contain cluster-scoped resources or resources that are
applied to other Namespaces. This Namespace is expected to exist.

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

[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 63
Required: \{\}
| +| `namespace` _string_ | namespace designates the kubernetes Namespace where bundle content
for the package, referenced in the 'packageName' field, will be applied and the necessary
service account can be found.
The bundle may contain cluster-scoped resources or resources that are
applied to other Namespaces. This Namespace is expected to exist.

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

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

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

When specified, it replaces the default preflight configuration for install/upgrade actions.
When not specified, the default configuration will be used. | | | @@ -158,7 +158,7 @@ ClusterExtensionList contains a list of ClusterExtension | `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | | | `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | | | `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `items` _[ClusterExtension](#clusterextension) array_ | | | Required: \{\}
| +| `items` _[ClusterExtension](#clusterextension) array_ | items is a required list of ClusterExtension objects. | | Required: \{\}
| #### ClusterExtensionSpec @@ -191,7 +191,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | The set of condition types which apply to all spec.source variations are Installed and Progressing.

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

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

When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition.
These are indications from a package owner to guide users away from a particular package, channel, or bundle.
BundleDeprecated is set if the requested bundle version is marked deprecated in the catalog.
ChannelDeprecated is set if the requested channel is marked deprecated in the catalog.
PackageDeprecated is set if the requested package is marked deprecated in the catalog.
Deprecated is a rollup condition that is present when any of the deprecated conditions are present. | | | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | The set of condition types which apply to all spec.source variations are Installed and Progressing.

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

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

When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition.
These are indications from a package owner to guide users away from a particular package, channel, or bundle.
BundleDeprecated is set if the requested bundle version is marked deprecated in the catalog.
ChannelDeprecated is set if the requested channel is marked deprecated in the catalog.
PackageDeprecated is set if the requested package is marked deprecated in the catalog.
Deprecated is a rollup condition that is present when any of the deprecated conditions are present. | | | | `install` _[ClusterExtensionInstallStatus](#clusterextensioninstallstatus)_ | install is a representation of the current installation status for this ClusterExtension. | | | diff --git a/internal/applier/helm.go b/internal/applier/helm.go index 97b75ed36..b38a7d82a 100644 --- a/internal/applier/helm.go +++ b/internal/applier/helm.go @@ -59,7 +59,7 @@ type Helm struct { // shouldSkipPreflight is a helper to determine if the preflight check is CRDUpgradeSafety AND // if it is set to enforcement None. -func shouldSkipPreflight(preflight Preflight, ctx context.Context, ext *ocv1alpha1.ClusterExtension, state string) bool { +func shouldSkipPreflight(ctx context.Context, preflight Preflight, ext *ocv1alpha1.ClusterExtension, state string) bool { l := log.FromContext(ctx) hasCRDUpgradeSafety := ext.Spec.Install.Preflight != nil && ext.Spec.Install.Preflight.CRDUpgradeSafety != nil _, isCRDUpgradeSafetyInstance := preflight.(*crdupgradesafety.Preflight) @@ -99,7 +99,7 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1alpha1.Clust } for _, preflight := range h.Preflights { - if shouldSkipPreflight(preflight, ctx, ext, state) { + if shouldSkipPreflight(ctx, preflight, ext, state) { continue } switch state { diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index c17390a57..47e85dd7e 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -687,7 +687,7 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { t.Log("By checking the expected progressing conditions") progressingCond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) require.NotNil(t, progressingCond) - require.Equal(t, metav1.ConditionFalse, progressingCond.Status) + require.Equal(t, metav1.ConditionTrue, progressingCond.Status) require.Equal(t, ocv1alpha1.ReasonSucceeded, progressingCond.Reason) require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{})) diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index cefe53913..1da2d8a13 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -83,14 +83,13 @@ func setInstallStatus(ext *ocv1alpha1.ClusterExtension, installStatus *ocv1alpha func setStatusProgressing(ext *ocv1alpha1.ClusterExtension, err error) { progressingCond := metav1.Condition{ Type: ocv1alpha1.TypeProgressing, - Status: metav1.ConditionFalse, + Status: metav1.ConditionTrue, Reason: ocv1alpha1.ReasonSucceeded, Message: "desired state reached", ObservedGeneration: ext.GetGeneration(), } if err != nil { - progressingCond.Status = metav1.ConditionTrue progressingCond.Reason = ocv1alpha1.ReasonRetrying progressingCond.Message = err.Error() } diff --git a/internal/controllers/common_controller_test.go b/internal/controllers/common_controller_test.go index 8f703fc6e..40aacce72 100644 --- a/internal/controllers/common_controller_test.go +++ b/internal/controllers/common_controller_test.go @@ -22,12 +22,12 @@ func TestSetStatusProgressing(t *testing.T) { expected metav1.Condition }{ { - name: "non-nil ClusterExtension, nil error, Progressing condition has status False with reason Success", + name: "non-nil ClusterExtension, nil error, Progressing condition has status True with reason Success", err: nil, clusterExtension: &ocv1alpha1.ClusterExtension{}, expected: metav1.Condition{ Type: ocv1alpha1.TypeProgressing, - Status: metav1.ConditionFalse, + Status: metav1.ConditionTrue, Reason: ocv1alpha1.ReasonSucceeded, Message: "desired state reached", }, From 1620b737c31c9d4fe05c616455f1582b990bcd94 Mon Sep 17 00:00:00 2001 From: Jordan Keister Date: Fri, 8 Nov 2024 15:55:20 -0600 Subject: [PATCH 3/3] e2e fixes Signed-off-by: Jordan Keister --- test/e2e/cluster_extension_install_test.go | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 884b46d1d..b1d8471ac 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -334,12 +334,12 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) }, pollDuration, pollInterval) - t.Log("By eventually reporting no longer progressing") + t.Log("By eventually reporting progressing as True") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -440,7 +440,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -496,7 +496,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -512,7 +512,7 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -547,7 +547,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -562,7 +562,7 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -607,7 +607,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -631,7 +631,7 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -690,7 +690,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -714,7 +714,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval) @@ -858,12 +858,12 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes } }, pollDuration, pollInterval) - t.Log("By eventually reporting Progressing == False with Reason Success") + t.Log("By eventually reporting Progressing == True with Reason Success") require.EventuallyWithT(t, func(ct *assert.CollectT) { assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing) if assert.NotNil(ct, cond) { - assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSucceeded, cond.Reason) } }, pollDuration, pollInterval)