Skip to content

Commit

Permalink
make extension and clusterextension api consistent
Browse files Browse the repository at this point in the history
Signed-off-by: Ankita Thomas <ankithom@redhat.com>
  • Loading branch information
ankitathomas committed May 9, 2024
1 parent 6d73b73 commit a934c27
Show file tree
Hide file tree
Showing 22 changed files with 775 additions and 394 deletions.
104 changes: 22 additions & 82 deletions api/v1alpha1/clusterextension_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,53 +18,35 @@ package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/operator-framework/operator-controller/internal/conditionsets"
)

type UpgradeConstraintPolicy string

const (
// The extension will only upgrade if the new version satisfies
// the upgrade constraints set by the package author.
UpgradeConstraintPolicyEnforce UpgradeConstraintPolicy = "Enforce"

// Unsafe option which allows an extension to be
// upgraded or downgraded to any available version of the package and
// ignore the upgrade path designed by package authors.
// This assumes that users independently verify the outcome of the changes.
// Use with caution as this can lead to unknown and potentially
// disastrous results such as data loss.
UpgradeConstraintPolicyIgnore UpgradeConstraintPolicy = "Ignore"
)
// +kubebuilder:validation:XValidation:rule="self.sourceType=='package' && has(self.__package__)",message="sourceType must match populated union field"
//
// ClusterExtensionSource defines the source for this ClusterExtension, right now, only a package is supported.
type ClusterExtensionSource struct {
//+kubebuilder:validation:Enum:=package
//+kubebuilder:validation:Required
// SourceType is the discriminator for the source type
SourceType string `json:"sourceType"`

// Package defines a reference for a bundle in a catalog defined by a name and a version and/or channel
Package *SourcePackage `json:"package,omitempty"`
}

// ClusterExtensionSpec defines the desired state of ClusterExtension
type ClusterExtensionSpec struct {
//+kubebuilder:validation:MaxLength:=48
//+kubebuilder:validation:Pattern:=^[a-z0-9]+(-[a-z0-9]+)*$
PackageName string `json:"packageName"`

//+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*)*$`
//+kubebuilder:Optional
// Version is an optional semver constraint on the package version. If not specified, the latest version available of the package will be installed.
// If specified, the specific version of the package will be installed so long as it is available in any of the content sources available.
// Examples: 1.2.3, 1.0.0-alpha, 1.0.0-rc.1
//
// For more information on semver, please see https://semver.org/
Version string `json:"version,omitempty"`
// paused controls the management state of the ClusterExtension. If paused, it will be ignored by the ClusterExtension controller.
Paused bool `json:"paused,omitempty"`

//+kubebuilder:validation:MaxLength:=48
//+kubebuilder:validation:Pattern:=^[a-z0-9]+([\.-][a-z0-9]+)*$
// Channel constraint definition
Channel string `json:"channel,omitempty"`
// source of ClusterExtension to be installed
Source ClusterExtensionSource `json:"source"`

//+kubebuilder:validation:Enum:=Enforce;Ignore
//+kubebuilder:default:=Enforce
//+kubebuilder:Optional
//
// Defines the policy for how to handle upgrade constraints
UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"`
// skipCRDUpgradeSafetyCheck specifies whether the CRD upgrade safety checks should be skipped when attempting to install the ClusterExtension
SkipCRDUpgradeSafetyCheck bool `json:"skipCRDUpgradeSafetyCheck,omitempty"`

//+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
//+kubebuilder:validation:MaxLength:=63
Expand All @@ -75,54 +57,11 @@ type ClusterExtensionSpec struct {
InstallNamespace string `json:"installNamespace"`
}

const (
// TODO(user): add more Types, here and into init()
TypeInstalled = "Installed"
TypeResolved = "Resolved"
// TypeDeprecated is a rollup condition that is present when
// any of the deprecated conditions are present.
TypeDeprecated = "Deprecated"
TypePackageDeprecated = "PackageDeprecated"
TypeChannelDeprecated = "ChannelDeprecated"
TypeBundleDeprecated = "BundleDeprecated"

ReasonBundleLookupFailed = "BundleLookupFailed"
ReasonInstallationFailed = "InstallationFailed"
ReasonInstallationStatusUnknown = "InstallationStatusUnknown"
ReasonInstallationSucceeded = "InstallationSucceeded"
ReasonInvalidSpec = "InvalidSpec"
ReasonResolutionFailed = "ResolutionFailed"
ReasonResolutionUnknown = "ResolutionUnknown"
ReasonSuccess = "Success"
ReasonDeprecated = "Deprecated"
)

func init() {
// TODO(user): add Types from above
conditionsets.ConditionTypes = append(conditionsets.ConditionTypes,
TypeInstalled,
TypeResolved,
TypeDeprecated,
TypePackageDeprecated,
TypeChannelDeprecated,
TypeBundleDeprecated,
)
// TODO(user): add Reasons from above
conditionsets.ConditionReasons = append(conditionsets.ConditionReasons,
ReasonInstallationSucceeded,
ReasonResolutionFailed,
ReasonResolutionUnknown,
ReasonBundleLookupFailed,
ReasonInstallationFailed,
ReasonInstallationStatusUnknown,
ReasonInvalidSpec,
ReasonSuccess,
ReasonDeprecated,
)
}

// ClusterExtensionStatus defines the observed state of ClusterExtension
type ClusterExtensionStatus struct {
// Paused indicates the current reconciliation state of this ClusterExtension
Paused bool `json:"paused"`

// +optional
InstalledBundle *BundleMetadata `json:"installedBundle,omitempty"`
// +optional
Expand All @@ -138,6 +77,7 @@ type ClusterExtensionStatus struct {
//+kubebuilder:object:root=true
//+kubebuilder:resource:scope=Cluster
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.paused`,description="The current reconciliation state of this ClusterExtension"

// ClusterExtension is the Schema for the clusterextensions API
type ClusterExtension struct {
Expand Down
109 changes: 109 additions & 0 deletions api/v1alpha1/common_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package v1alpha1

import "github.com/operator-framework/operator-controller/internal/conditionsets"

const (
TypeInstalled = "Installed"
TypeResolved = "Resolved"
// TypeDeprecated is a rollup condition that is present when
// any of the deprecated conditions are present.
TypeDeprecated = "Deprecated"
TypePackageDeprecated = "PackageDeprecated"
TypeChannelDeprecated = "ChannelDeprecated"
TypeBundleDeprecated = "BundleDeprecated"
// TypeProgressing indicates whether operator-controller is
// reconciling, installing, updating or deleting a ClusterExtension or Extension.
TypeProgressing = "Progressing"
// TODO(user): add more Types, here and into init()

ReasonBundleLookupFailed = "BundleLookupFailed"
ReasonInstallationFailed = "InstallationFailed"
ReasonInstallationStatusUnknown = "InstallationStatusUnknown"
ReasonInstallationSucceeded = "InstallationSucceeded"
ReasonInvalidSpec = "InvalidSpec"
ReasonResolutionFailed = "ResolutionFailed"
ReasonResolutionUnknown = "ResolutionUnknown"
ReasonSuccess = "Success"
ReasonDeprecated = "Deprecated"
ReasonProgressing = "Progressing"
ReasonFailedToReachDesiredIntent = "FailedToReachDesiredIntent"
ReasonReachedDesiredIntent = "ReachedDesiredIntent"
)

func init() {
// TODO(user): add Types from above
conditionsets.ConditionTypes = append(conditionsets.ConditionTypes,
TypeInstalled,
TypeResolved,
TypeDeprecated,
TypePackageDeprecated,
TypeChannelDeprecated,
TypeBundleDeprecated,
TypeProgressing,
)
// TODO(user): add Reasons from above
conditionsets.ConditionReasons = append(conditionsets.ConditionReasons,
ReasonInstallationSucceeded,
ReasonResolutionFailed,
ReasonResolutionUnknown,
ReasonBundleLookupFailed,
ReasonInstallationFailed,
ReasonInstallationStatusUnknown,
ReasonInvalidSpec,
ReasonSuccess,
ReasonDeprecated,
ReasonProgressing,
ReasonFailedToReachDesiredIntent,
ReasonReachedDesiredIntent,
)
}

const (
SourceTypePackage = "package"
)

type UpgradeConstraintPolicy string

const (
// The UpgradeConstraintPolicyEnforce policy only allows upgrades if
// the new version satisfies the upgrade constraints set by the package author.
UpgradeConstraintPolicyEnforce UpgradeConstraintPolicy = "Enforce"

// The UpgradeConstraintPolicyIgnore policy allows upgrades or downgrades
// to any available version of the package, ignoring any upgrade paths
// designed by package authors.
// This assumes that users independently verify the outcome of the changes.
// Use with caution as this can lead to unknown and potentially
// disastrous results such as data loss.
UpgradeConstraintPolicyIgnore UpgradeConstraintPolicy = "Ignore"
)

type SourcePackage struct {
//+kubebuilder:validation:MaxLength:=48
//+kubebuilder:validation:Pattern:=^[a-z0-9]+(-[a-z0-9]+)*$
// name specifies the name of the name of the package
Name string `json:"name"`

//+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*)*$`
//+kubebuilder:Optional
// Version is an optional semver constraint on the package version. If not specified, the latest version available of the package will be installed.
// If specified, the specific version of the package will be installed so long as it is available in any of the content sources available.
// Examples: 1.2.3, 1.0.0-alpha, 1.0.0-rc.1
//
// For more information on semver, please see https://semver.org/
// version constraint definition
Version string `json:"version,omitempty"`

//+kubebuilder:validation:MaxLength:=48
//+kubebuilder:validation:Pattern:=^[a-z0-9]+([\.-][a-z0-9]+)*$
// channel constraint definition
Channel string `json:"channel,omitempty"`

//+kubebuilder:validation:Enum:=Enforce;Ignore
//+kubebuilder:default:=Enforce
//+kubebuilder:Optional
//
// upgradeConstraintPolicy Defines the policy for how to handle upgrade constraints
UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"`
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import (
"github.com/operator-framework/operator-controller/internal/conditionsets"
)

func TestClusterExtensionTypeRegistration(t *testing.T) {
func TestConditionTypeRegistration(t *testing.T) {
types, err := parseConstants("Type")
if err != nil {
t.Fatalf("unable to parse Type constants %v", err)
}

for _, tt := range types {
if !slices.Contains(conditionsets.ConditionTypes, tt) && !slices.Contains(conditionsets.ExtensionConditionTypes, tt) {
if !slices.Contains(conditionsets.ConditionTypes, tt) {
t.Errorf("append Type%s to conditionsets.ConditionTypes in this package's init function", tt)
}
}
Expand All @@ -32,21 +32,16 @@ func TestClusterExtensionTypeRegistration(t *testing.T) {
t.Errorf("there must be a Type%[1]s string literal constant for type %[1]q (i.e. 'const Type%[1]s = %[1]q')", tt)
}
}
for _, tt := range conditionsets.ExtensionConditionTypes {
if !slices.Contains(types, tt) {
t.Errorf("there must be a Type%[1]s string literal constant for type %[1]q (i.e. 'const Type%[1]s = %[1]q')", tt)
}
}
}

func TestClusterExtensionReasonRegistration(t *testing.T) {
func TestConditionReasonRegistration(t *testing.T) {
reasons, err := parseConstants("Reason")
if err != nil {
t.Fatalf("unable to parse Reason constants %v", err)
}

for _, r := range reasons {
if !slices.Contains(conditionsets.ConditionReasons, r) && !slices.Contains(conditionsets.ExtensionConditionReasons, r) {
if !slices.Contains(conditionsets.ConditionReasons, r) {
t.Errorf("append Reason%s to conditionsets.ConditionReasons in this package's init function.", r)
}
}
Expand All @@ -55,11 +50,6 @@ func TestClusterExtensionReasonRegistration(t *testing.T) {
t.Errorf("there must be a Reason%[1]s string literal constant for reason %[1]q (i.e. 'const Reason%[1]s = %[1]q')", r)
}
}
for _, r := range conditionsets.ExtensionConditionReasons {
if !slices.Contains(reasons, r) {
t.Errorf("there must be a Reason%[1]s string literal constant for reason %[1]q (i.e. 'const Reason%[1]s = %[1]q')", r)
}
}
}

// parseConstants parses the values of the top-level constants in the current
Expand Down
57 changes: 1 addition & 56 deletions api/v1alpha1/extension_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,44 +18,8 @@ package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/operator-framework/operator-controller/internal/conditionsets"
)

const (
SourceTypePackage = "package"
)

type ExtensionSourcePackage struct {
//+kubebuilder:validation:MaxLength:=48
//+kubebuilder:validation:Pattern:=^[a-z0-9]+(-[a-z0-9]+)*$
// name specifies the name of the name of the package
Name string `json:"name"`

//+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*)*$`
//+kubebuilder:Optional
// Version is an optional semver constraint on the package version. If not specified, the latest version available of the package will be installed.
// If specified, the specific version of the package will be installed so long as it is available in any of the content sources available.
// Examples: 1.2.3, 1.0.0-alpha, 1.0.0-rc.1
//
// For more information on semver, please see https://semver.org/
// version constraint definition
Version string `json:"version,omitempty"`

//+kubebuilder:validation:MaxLength:=48
//+kubebuilder:validation:Pattern:=^[a-z0-9]+([\.-][a-z0-9]+)*$
// channel constraint definition
Channel string `json:"channel,omitempty"`

//+kubebuilder:validation:Enum:=Enforce;Ignore
//+kubebuilder:default:=Enforce
//+kubebuilder:Optional
//
// upgradeConstraintPolicy Defines the policy for how to handle upgrade constraints
UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"`
}

// +kubebuilder:validation:XValidation:rule="self.sourceType=='package' && has(self.__package__)",message="sourceType must match populated union field"
//
// ExtensionSource defines the source for this Extension, right now, only a package is supported.
Expand All @@ -66,7 +30,7 @@ type ExtensionSource struct {
SourceType string `json:"sourceType"`

// package defines a reference for a bundle in a catalog defined by a name and a version and/or channel
Package *ExtensionSourcePackage `json:"package,omitempty"`
Package *SourcePackage `json:"package,omitempty"`
}

// ExtensionSpec defines the desired state of Extension
Expand Down Expand Up @@ -135,25 +99,6 @@ type ExtensionList struct {
Items []Extension `json:"items"`
}

const (
// TypeProgressing indicates whether operator-controller is
// reconciling, installing, updating or deleting an extension.
TypeProgressing = "Progressing"

ReasonProgressing = "Progressing"
ReasonFailedToReachDesiredIntent = "FailedToReachDesiredIntent"
ReasonReachedDesiredIntent = "ReachedDesiredIntent"
)

func init() {
SchemeBuilder.Register(&Extension{}, &ExtensionList{})

conditionsets.ExtensionConditionTypes = []string{
TypeProgressing,
}
conditionsets.ExtensionConditionReasons = []string{
ReasonProgressing,
ReasonFailedToReachDesiredIntent,
ReasonReachedDesiredIntent,
}
}
Loading

0 comments on commit a934c27

Please sign in to comment.