diff --git a/Dockerfiles/Dockerfile b/Dockerfiles/Dockerfile index ac3797f304a..ba86d321020 100644 --- a/Dockerfiles/Dockerfile +++ b/Dockerfiles/Dockerfile @@ -38,7 +38,7 @@ COPY main.go main.go COPY pkg/ pkg/ # Build -RUN CGO_ENABLED=${CGO_ENABLED} GOOS=linux GOARCH=amd64 go build -a -o manager main.go +RUN CGO_ENABLED=${CGO_ENABLED} GOOS=linux GOARCH=${TARGETARCH} go build -a -o manager main.go ################################################################################ FROM registry.access.redhat.com/ubi8/ubi-minimal:latest diff --git a/Makefile b/Makefile index 7247c7dd11a..c96e64390aa 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # To re-generate a bundle for another specific version without changing the standard setup, you can: # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) -VERSION ?= 2.21.0 +VERSION ?= 2.23.1 # IMAGE_TAG_BASE defines the opendatahub.io namespace and part of the image name for remote images. # This variable is used to construct full image tags for bundle and catalog images. # diff --git a/README.md b/README.md index fcb3c24c64a..ef7f779bd22 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ and configure these applications. - [Usage](#usage) - [Prerequisites](#prerequisites) - [Installation](#installation) + - [Configuration](#configuration) - [Developer Guide](#developer-guide) - [Pre-requisites](#pre-requisites) - [Download manifests](#download-manifests) @@ -74,6 +75,26 @@ Additionally installing `Authorino operator` & `Service Mesh operator` enhances 3. Create [DataScienceCluster](#example-datasciencecluster) CR to enable components + +### Configuration + +- in ODH 2.23.1, we introduced a new feature which allows user to use their own application namespace than default one "opendatahub". + +1. for new cluster, as this cluster has not been used for ODH or RHOAI. + Here we use namespace A for example as targeted application namespace, please follow below steps before install ODH operator: + + - create namespace A + - add label `opendatahub.io/application-namespace: true` onto namespace A. Only one namespace in the cluster can have this label. + - install ODH operator either from UI or by GitOps/CLI + - once Operator is up and running, manually create DSCI CR by set `.spec.applicationsNamespace:A` + - wait till DSCI status update to "Ready" + - continue to create DSC CR + +2. for upgrade case, as ODH is running in the cluster. + + Be aware: to switch to a different application namespace can cause more issues and require manual cleanup, therefore we suggest this to be done for new cluster. + + ## Developer Guide #### Pre-requisites @@ -409,7 +430,7 @@ Please refer to [api documentation](docs/api-overview.md) ### Component Integration -Please refer to [components docs](components/README.md) +Please refer to [components docs](docs/COMPONENT_INTEGRATION.md) ### Troubleshooting diff --git a/apis/common/types.go b/apis/common/types.go index 44542d15895..d3091735976 100644 --- a/apis/common/types.go +++ b/apis/common/types.go @@ -2,6 +2,7 @@ package common import ( operatorv1 "github.com/openshift/api/operator/v1" + "github.com/operator-framework/api/pkg/lib/version" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -70,6 +71,16 @@ type Status struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } +// ComponentReleaseStatus represents the detailed status of a component release. +// +kubebuilder:object:generate=true +type ComponentReleaseStatus struct { + // +required + // +kubebuilder:validation:Required + Name string `json:"name"` + Version version.OperatorVersion `json:"version,omitempty"` + RepoURL string `json:"repoUrl,omitempty"` +} + type WithStatus interface { GetStatus() *Status } @@ -78,6 +89,11 @@ type WithDevFlags interface { GetDevFlags() *DevFlags } +type WithReleases interface { + GetReleaseStatus() *[]ComponentReleaseStatus + SetReleaseStatus(status []ComponentReleaseStatus) +} + type PlatformObject interface { client.Object WithStatus diff --git a/apis/common/zz_generated.deepcopy.go b/apis/common/zz_generated.deepcopy.go index b55b115b354..4cbd11b14aa 100644 --- a/apis/common/zz_generated.deepcopy.go +++ b/apis/common/zz_generated.deepcopy.go @@ -24,6 +24,22 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ComponentReleaseStatus) DeepCopyInto(out *ComponentReleaseStatus) { + *out = *in + in.Version.DeepCopyInto(&out.Version) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentReleaseStatus. +func (in *ComponentReleaseStatus) DeepCopy() *ComponentReleaseStatus { + if in == nil { + return nil + } + out := new(ComponentReleaseStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DevFlags) DeepCopyInto(out *DevFlags) { *out = *in diff --git a/apis/components/v1alpha1/codeflare_types.go b/apis/components/v1alpha1/codeflare_types.go index 9152db29fd0..7106ee5833e 100644 --- a/apis/components/v1alpha1/codeflare_types.go +++ b/apis/components/v1alpha1/codeflare_types.go @@ -30,6 +30,11 @@ const ( // CodeFlareCommonStatus defines the shared observed state of CodeFlare type CodeFlareCommonStatus struct { + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // CodeFlareStatus defines the observed state of CodeFlare @@ -70,6 +75,12 @@ func (c *CodeFlare) GetStatus() *common.Status { return &c.Status.Status } +func (c *CodeFlare) GetReleaseStatus() *[]common.ComponentReleaseStatus { return &c.Status.Releases } + +func (c *CodeFlare) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + func init() { SchemeBuilder.Register(&CodeFlare{}, &CodeFlareList{}) } diff --git a/apis/components/v1alpha1/dashboard_types.go b/apis/components/v1alpha1/dashboard_types.go index 14afd14337b..86e5a76537c 100644 --- a/apis/components/v1alpha1/dashboard_types.go +++ b/apis/components/v1alpha1/dashboard_types.go @@ -46,6 +46,11 @@ type DashboardSpec struct { // DashboardCommonStatus defines the shared observed state of Dashboard type DashboardCommonStatus struct { URL string `json:"url,omitempty"` + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // DashboardStatus defines the observed state of Dashboard @@ -79,6 +84,12 @@ func (c *Dashboard) GetStatus() *common.Status { return &c.Status.Status } +func (c *Dashboard) GetReleaseStatus() *[]common.ComponentReleaseStatus { return &c.Status.Releases } + +func (c *Dashboard) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // +kubebuilder:object:root=true // DashboardList contains a list of Dashboard diff --git a/apis/components/v1alpha1/datasciencepipelines_types.go b/apis/components/v1alpha1/datasciencepipelines_types.go index da10f02cc8c..7c19d694be5 100644 --- a/apis/components/v1alpha1/datasciencepipelines_types.go +++ b/apis/components/v1alpha1/datasciencepipelines_types.go @@ -55,6 +55,11 @@ type DataSciencePipelinesCommonSpec struct { // DataSciencePipelinesCommonStatus defines the shared observed state of DataSciencePipelines type DataSciencePipelinesCommonStatus struct { + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // DataSciencePipelinesStatus defines the observed state of DataSciencePipelines @@ -71,6 +76,14 @@ func (c *DataSciencePipelines) GetStatus() *common.Status { return &c.Status.Status } +func (c *DataSciencePipelines) GetReleaseStatus() *[]common.ComponentReleaseStatus { + return &c.Status.Releases +} + +func (c *DataSciencePipelines) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // +kubebuilder:object:root=true // DataSciencePipelinesList contains a list of DataSciencePipelines diff --git a/apis/components/v1alpha1/kserve_types.go b/apis/components/v1alpha1/kserve_types.go index b69d88aed83..ff1d38de57e 100644 --- a/apis/components/v1alpha1/kserve_types.go +++ b/apis/components/v1alpha1/kserve_types.go @@ -77,6 +77,11 @@ type KserveCommonStatus struct { // DefaultDeploymentMode is the value of the defaultDeploymentMode field // as read from the "deploy" JSON in the inferenceservice-config ConfigMap DefaultDeploymentMode string `json:"defaultDeploymentMode,omitempty"` + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // KserveStatus defines the observed state of Kserve @@ -109,6 +114,14 @@ func (c *Kserve) GetStatus() *common.Status { return &c.Status.Status } +func (c *Kserve) GetReleaseStatus() *[]common.ComponentReleaseStatus { + return &c.Status.Releases +} + +func (c *Kserve) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // +kubebuilder:object:root=true // KserveList contains a list of Kserve diff --git a/apis/components/v1alpha1/kueue_types.go b/apis/components/v1alpha1/kueue_types.go index e0be9ff57ac..d70c1faf64b 100644 --- a/apis/components/v1alpha1/kueue_types.go +++ b/apis/components/v1alpha1/kueue_types.go @@ -57,6 +57,11 @@ type KueueCommonSpec struct { // KueueCommonStatus defines the shared observed state of Kueue type KueueCommonStatus struct { + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // KueueStatus defines the observed state of Kueue @@ -80,10 +85,17 @@ func init() { func (c *Kueue) GetDevFlags() *common.DevFlags { return c.Spec.DevFlags } + func (c *Kueue) GetStatus() *common.Status { return &c.Status.Status } +func (c *Kueue) GetReleaseStatus() *[]common.ComponentReleaseStatus { return &c.Status.Releases } + +func (c *Kueue) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // DSCKueue contains all the configuration exposed in DSC instance for Kueue component type DSCKueue struct { common.ManagementSpec `json:",inline"` diff --git a/apis/components/v1alpha1/modelmeshserving_types.go b/apis/components/v1alpha1/modelmeshserving_types.go index e28649a417c..bc86ec26b90 100644 --- a/apis/components/v1alpha1/modelmeshserving_types.go +++ b/apis/components/v1alpha1/modelmeshserving_types.go @@ -57,6 +57,11 @@ type ModelMeshServingCommonSpec struct { // ModelMeshServingCommonStatus defines the shared observed state of ModelMeshServing type ModelMeshServingCommonStatus struct { + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // ModelMeshServingStatus defines the observed state of ModelMeshServing @@ -80,10 +85,19 @@ func init() { func (c *ModelMeshServing) GetDevFlags() *common.DevFlags { return c.Spec.DevFlags } + func (c *ModelMeshServing) GetStatus() *common.Status { return &c.Status.Status } +func (c *ModelMeshServing) GetReleaseStatus() *[]common.ComponentReleaseStatus { + return &c.Status.Releases +} + +func (c *ModelMeshServing) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // DSCModelMeshServing contains all the configuration exposed in DSC instance for ModelMeshServing component type DSCModelMeshServing struct { common.ManagementSpec `json:",inline"` diff --git a/apis/components/v1alpha1/modelregistry_types.go b/apis/components/v1alpha1/modelregistry_types.go index 0d653b7d355..b7b9432272b 100644 --- a/apis/components/v1alpha1/modelregistry_types.go +++ b/apis/components/v1alpha1/modelregistry_types.go @@ -50,7 +50,8 @@ type ModelRegistrySpec struct { // ModelRegistryCommonStatus defines the shared observed state of ModelRegistry type ModelRegistryCommonStatus struct { - RegistriesNamespace string `json:"registriesNamespace,omitempty"` + RegistriesNamespace string `json:"registriesNamespace,omitempty"` + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // ModelRegistryStatus defines the observed state of ModelRegistry @@ -83,6 +84,14 @@ func (c *ModelRegistry) GetStatus() *common.Status { return &c.Status.Status } +func (c *ModelRegistry) GetReleaseStatus() *[]common.ComponentReleaseStatus { + return &c.Status.Releases +} + +func (c *ModelRegistry) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // +kubebuilder:object:root=true // ModelRegistryList contains a list of ModelRegistry diff --git a/apis/components/v1alpha1/ray_types.go b/apis/components/v1alpha1/ray_types.go index a51c29e3cd4..9d26bb5289a 100644 --- a/apis/components/v1alpha1/ray_types.go +++ b/apis/components/v1alpha1/ray_types.go @@ -57,6 +57,11 @@ type RayCommonSpec struct { // RayCommonStatus defines the shared observed state of Ray type RayCommonStatus struct { + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // RayStatus defines the observed state of Ray @@ -80,10 +85,17 @@ func init() { func (c *Ray) GetDevFlags() *common.DevFlags { return c.Spec.DevFlags } + func (c *Ray) GetStatus() *common.Status { return &c.Status.Status } +func (c *Ray) GetReleaseStatus() *[]common.ComponentReleaseStatus { return &c.Status.Releases } + +func (c *Ray) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // DSCRay contains all the configuration exposed in DSC instance for Ray component type DSCRay struct { common.ManagementSpec `json:",inline"` diff --git a/apis/components/v1alpha1/trainingoperator_types.go b/apis/components/v1alpha1/trainingoperator_types.go index 2d76ca78e6e..2b9040fa45f 100644 --- a/apis/components/v1alpha1/trainingoperator_types.go +++ b/apis/components/v1alpha1/trainingoperator_types.go @@ -57,6 +57,11 @@ type TrainingOperatorCommonSpec struct { // TrainingOperatorCommonStatus defines the shared observed state of TrainingOperator type TrainingOperatorCommonStatus struct { + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // TrainingOperatorStatus defines the observed state of TrainingOperator @@ -80,10 +85,19 @@ func init() { func (c *TrainingOperator) GetDevFlags() *common.DevFlags { return c.Spec.DevFlags } + func (c *TrainingOperator) GetStatus() *common.Status { return &c.Status.Status } +func (c *TrainingOperator) GetReleaseStatus() *[]common.ComponentReleaseStatus { + return &c.Status.Releases +} + +func (c *TrainingOperator) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // DSCTrainingOperator contains all the configuration exposed in DSC instance for TrainingOperator component type DSCTrainingOperator struct { common.ManagementSpec `json:",inline"` diff --git a/apis/components/v1alpha1/trustyai_types.go b/apis/components/v1alpha1/trustyai_types.go index 9ac2652fcc1..185e1bec0c0 100644 --- a/apis/components/v1alpha1/trustyai_types.go +++ b/apis/components/v1alpha1/trustyai_types.go @@ -57,6 +57,11 @@ type TrustyAICommonSpec struct { // TrustyAICommonStatus defines the shared observed state of TrustyAI type TrustyAICommonStatus struct { + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // TrustyAIStatus defines the observed state of TrustyAI @@ -80,10 +85,17 @@ func init() { func (c *TrustyAI) GetDevFlags() *common.DevFlags { return c.Spec.DevFlags } + func (c *TrustyAI) GetStatus() *common.Status { return &c.Status.Status } +func (c *TrustyAI) GetReleaseStatus() *[]common.ComponentReleaseStatus { return &c.Status.Releases } + +func (c *TrustyAI) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // DSCTrustyAI contains all the configuration exposed in DSC instance for TrustyAI component type DSCTrustyAI struct { common.ManagementSpec `json:",inline"` diff --git a/apis/components/v1alpha1/workbenches_types.go b/apis/components/v1alpha1/workbenches_types.go index 4ea01ce7bbc..4ca40339930 100644 --- a/apis/components/v1alpha1/workbenches_types.go +++ b/apis/components/v1alpha1/workbenches_types.go @@ -44,6 +44,11 @@ type WorkbenchesSpec struct { // WorkbenchesCommonStatus defines the shared observed state of Workbenches type WorkbenchesCommonStatus struct { + // +patchMergeKey=name + // +patchStrategy=merge + // +listType=map + // +listMapKey=name + Releases []common.ComponentReleaseStatus `json:"releases,omitempty"` } // WorkbenchesStatus defines the observed state of Workbenches @@ -76,6 +81,12 @@ func (c *Workbenches) GetStatus() *common.Status { return &c.Status.Status } +func (c *Workbenches) GetReleaseStatus() *[]common.ComponentReleaseStatus { return &c.Status.Releases } + +func (c *Workbenches) SetReleaseStatus(releases []common.ComponentReleaseStatus) { + c.Status.Releases = releases +} + // +kubebuilder:object:root=true // WorkbenchesList contains a list of Workbenches diff --git a/apis/components/v1alpha1/zz_generated.deepcopy.go b/apis/components/v1alpha1/zz_generated.deepcopy.go index 9de0b610abc..4bd4e09cadc 100644 --- a/apis/components/v1alpha1/zz_generated.deepcopy.go +++ b/apis/components/v1alpha1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1alpha1 import ( + "github.com/opendatahub-io/opendatahub-operator/v2/apis/common" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -70,6 +71,13 @@ func (in *CodeFlareCommonSpec) DeepCopy() *CodeFlareCommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CodeFlareCommonStatus) DeepCopyInto(out *CodeFlareCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CodeFlareCommonStatus. @@ -134,7 +142,7 @@ func (in *CodeFlareSpec) DeepCopy() *CodeFlareSpec { func (in *CodeFlareStatus) DeepCopyInto(out *CodeFlareStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.CodeFlareCommonStatus = in.CodeFlareCommonStatus + in.CodeFlareCommonStatus.DeepCopyInto(&out.CodeFlareCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CodeFlareStatus. @@ -171,7 +179,7 @@ func (in *DSCCodeFlareStatus) DeepCopyInto(out *DSCCodeFlareStatus) { if in.CodeFlareCommonStatus != nil { in, out := &in.CodeFlareCommonStatus, &out.CodeFlareCommonStatus *out = new(CodeFlareCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -209,7 +217,7 @@ func (in *DSCDashboardStatus) DeepCopyInto(out *DSCDashboardStatus) { if in.DashboardCommonStatus != nil { in, out := &in.DashboardCommonStatus, &out.DashboardCommonStatus *out = new(DashboardCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -247,7 +255,7 @@ func (in *DSCDataSciencePipelinesStatus) DeepCopyInto(out *DSCDataSciencePipelin if in.DataSciencePipelinesCommonStatus != nil { in, out := &in.DataSciencePipelinesCommonStatus, &out.DataSciencePipelinesCommonStatus *out = new(DataSciencePipelinesCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -285,7 +293,7 @@ func (in *DSCKserveStatus) DeepCopyInto(out *DSCKserveStatus) { if in.KserveCommonStatus != nil { in, out := &in.KserveCommonStatus, &out.KserveCommonStatus *out = new(KserveCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -323,7 +331,7 @@ func (in *DSCKueueStatus) DeepCopyInto(out *DSCKueueStatus) { if in.KueueCommonStatus != nil { in, out := &in.KueueCommonStatus, &out.KueueCommonStatus *out = new(KueueCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -361,7 +369,7 @@ func (in *DSCModelMeshServingStatus) DeepCopyInto(out *DSCModelMeshServingStatus if in.ModelMeshServingCommonStatus != nil { in, out := &in.ModelMeshServingCommonStatus, &out.ModelMeshServingCommonStatus *out = new(ModelMeshServingCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -399,7 +407,7 @@ func (in *DSCModelRegistryStatus) DeepCopyInto(out *DSCModelRegistryStatus) { if in.ModelRegistryCommonStatus != nil { in, out := &in.ModelRegistryCommonStatus, &out.ModelRegistryCommonStatus *out = new(ModelRegistryCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -437,7 +445,7 @@ func (in *DSCRayStatus) DeepCopyInto(out *DSCRayStatus) { if in.RayCommonStatus != nil { in, out := &in.RayCommonStatus, &out.RayCommonStatus *out = new(RayCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -475,7 +483,7 @@ func (in *DSCTrainingOperatorStatus) DeepCopyInto(out *DSCTrainingOperatorStatus if in.TrainingOperatorCommonStatus != nil { in, out := &in.TrainingOperatorCommonStatus, &out.TrainingOperatorCommonStatus *out = new(TrainingOperatorCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -513,7 +521,7 @@ func (in *DSCTrustyAIStatus) DeepCopyInto(out *DSCTrustyAIStatus) { if in.TrustyAICommonStatus != nil { in, out := &in.TrustyAICommonStatus, &out.TrustyAICommonStatus *out = new(TrustyAICommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -551,7 +559,7 @@ func (in *DSCWorkbenchesStatus) DeepCopyInto(out *DSCWorkbenchesStatus) { if in.WorkbenchesCommonStatus != nil { in, out := &in.WorkbenchesCommonStatus, &out.WorkbenchesCommonStatus *out = new(WorkbenchesCommonStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -611,6 +619,13 @@ func (in *DashboardCommonSpec) DeepCopy() *DashboardCommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DashboardCommonStatus) DeepCopyInto(out *DashboardCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardCommonStatus. @@ -675,7 +690,7 @@ func (in *DashboardSpec) DeepCopy() *DashboardSpec { func (in *DashboardStatus) DeepCopyInto(out *DashboardStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.DashboardCommonStatus = in.DashboardCommonStatus + in.DashboardCommonStatus.DeepCopyInto(&out.DashboardCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardStatus. @@ -734,6 +749,13 @@ func (in *DataSciencePipelinesCommonSpec) DeepCopy() *DataSciencePipelinesCommon // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DataSciencePipelinesCommonStatus) DeepCopyInto(out *DataSciencePipelinesCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataSciencePipelinesCommonStatus. @@ -798,7 +820,7 @@ func (in *DataSciencePipelinesSpec) DeepCopy() *DataSciencePipelinesSpec { func (in *DataSciencePipelinesStatus) DeepCopyInto(out *DataSciencePipelinesStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.DataSciencePipelinesCommonStatus = in.DataSciencePipelinesCommonStatus + in.DataSciencePipelinesCommonStatus.DeepCopyInto(&out.DataSciencePipelinesCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataSciencePipelinesStatus. @@ -859,6 +881,13 @@ func (in *KserveCommonSpec) DeepCopy() *KserveCommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KserveCommonStatus) DeepCopyInto(out *KserveCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KserveCommonStatus. @@ -923,7 +952,7 @@ func (in *KserveSpec) DeepCopy() *KserveSpec { func (in *KserveStatus) DeepCopyInto(out *KserveStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.KserveCommonStatus = in.KserveCommonStatus + in.KserveCommonStatus.DeepCopyInto(&out.KserveCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KserveStatus. @@ -982,6 +1011,13 @@ func (in *KueueCommonSpec) DeepCopy() *KueueCommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KueueCommonStatus) DeepCopyInto(out *KueueCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KueueCommonStatus. @@ -1046,7 +1082,7 @@ func (in *KueueSpec) DeepCopy() *KueueSpec { func (in *KueueStatus) DeepCopyInto(out *KueueStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.KueueCommonStatus = in.KueueCommonStatus + in.KueueCommonStatus.DeepCopyInto(&out.KueueCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KueueStatus. @@ -1238,6 +1274,13 @@ func (in *ModelMeshServingCommonSpec) DeepCopy() *ModelMeshServingCommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ModelMeshServingCommonStatus) DeepCopyInto(out *ModelMeshServingCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModelMeshServingCommonStatus. @@ -1302,7 +1345,7 @@ func (in *ModelMeshServingSpec) DeepCopy() *ModelMeshServingSpec { func (in *ModelMeshServingStatus) DeepCopyInto(out *ModelMeshServingStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.ModelMeshServingCommonStatus = in.ModelMeshServingCommonStatus + in.ModelMeshServingCommonStatus.DeepCopyInto(&out.ModelMeshServingCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModelMeshServingStatus. @@ -1361,6 +1404,13 @@ func (in *ModelRegistryCommonSpec) DeepCopy() *ModelRegistryCommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ModelRegistryCommonStatus) DeepCopyInto(out *ModelRegistryCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModelRegistryCommonStatus. @@ -1425,7 +1475,7 @@ func (in *ModelRegistrySpec) DeepCopy() *ModelRegistrySpec { func (in *ModelRegistryStatus) DeepCopyInto(out *ModelRegistryStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.ModelRegistryCommonStatus = in.ModelRegistryCommonStatus + in.ModelRegistryCommonStatus.DeepCopyInto(&out.ModelRegistryCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModelRegistryStatus. @@ -1499,6 +1549,13 @@ func (in *RayCommonSpec) DeepCopy() *RayCommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RayCommonStatus) DeepCopyInto(out *RayCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RayCommonStatus. @@ -1563,7 +1620,7 @@ func (in *RaySpec) DeepCopy() *RaySpec { func (in *RayStatus) DeepCopyInto(out *RayStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.RayCommonStatus = in.RayCommonStatus + in.RayCommonStatus.DeepCopyInto(&out.RayCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RayStatus. @@ -1622,6 +1679,13 @@ func (in *TrainingOperatorCommonSpec) DeepCopy() *TrainingOperatorCommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TrainingOperatorCommonStatus) DeepCopyInto(out *TrainingOperatorCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrainingOperatorCommonStatus. @@ -1686,7 +1750,7 @@ func (in *TrainingOperatorSpec) DeepCopy() *TrainingOperatorSpec { func (in *TrainingOperatorStatus) DeepCopyInto(out *TrainingOperatorStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.TrainingOperatorCommonStatus = in.TrainingOperatorCommonStatus + in.TrainingOperatorCommonStatus.DeepCopyInto(&out.TrainingOperatorCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrainingOperatorStatus. @@ -1745,6 +1809,13 @@ func (in *TrustyAICommonSpec) DeepCopy() *TrustyAICommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TrustyAICommonStatus) DeepCopyInto(out *TrustyAICommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrustyAICommonStatus. @@ -1809,7 +1880,7 @@ func (in *TrustyAISpec) DeepCopy() *TrustyAISpec { func (in *TrustyAIStatus) DeepCopyInto(out *TrustyAIStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.TrustyAICommonStatus = in.TrustyAICommonStatus + in.TrustyAICommonStatus.DeepCopyInto(&out.TrustyAICommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrustyAIStatus. @@ -1868,6 +1939,13 @@ func (in *WorkbenchesCommonSpec) DeepCopy() *WorkbenchesCommonSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkbenchesCommonStatus) DeepCopyInto(out *WorkbenchesCommonStatus) { *out = *in + if in.Releases != nil { + in, out := &in.Releases, &out.Releases + *out = make([]common.ComponentReleaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkbenchesCommonStatus. @@ -1932,7 +2010,7 @@ func (in *WorkbenchesSpec) DeepCopy() *WorkbenchesSpec { func (in *WorkbenchesStatus) DeepCopyInto(out *WorkbenchesStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - out.WorkbenchesCommonStatus = in.WorkbenchesCommonStatus + in.WorkbenchesCommonStatus.DeepCopyInto(&out.WorkbenchesCommonStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkbenchesStatus. diff --git a/bundle/manifests/components.platform.opendatahub.io_codeflares.yaml b/bundle/manifests/components.platform.opendatahub.io_codeflares.yaml index caec1b33965..6d2f77fc8e6 100644 --- a/bundle/manifests/components.platform.opendatahub.io_codeflares.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_codeflares.yaml @@ -141,6 +141,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/components.platform.opendatahub.io_dashboards.yaml b/bundle/manifests/components.platform.opendatahub.io_dashboards.yaml index 335119bc596..7b75e226316 100644 --- a/bundle/manifests/components.platform.opendatahub.io_dashboards.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_dashboards.yaml @@ -146,6 +146,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map url: type: string type: object diff --git a/bundle/manifests/components.platform.opendatahub.io_datasciencepipelines.yaml b/bundle/manifests/components.platform.opendatahub.io_datasciencepipelines.yaml index 458b38f5e52..a5a0d8a3187 100644 --- a/bundle/manifests/components.platform.opendatahub.io_datasciencepipelines.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_datasciencepipelines.yaml @@ -144,6 +144,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/components.platform.opendatahub.io_kserves.yaml b/bundle/manifests/components.platform.opendatahub.io_kserves.yaml index 92ac4530345..635d96d221e 100644 --- a/bundle/manifests/components.platform.opendatahub.io_kserves.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_kserves.yaml @@ -226,6 +226,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/components.platform.opendatahub.io_kueues.yaml b/bundle/manifests/components.platform.opendatahub.io_kueues.yaml index 87ada19027e..aba0b20b980 100644 --- a/bundle/manifests/components.platform.opendatahub.io_kueues.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_kueues.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/components.platform.opendatahub.io_modelmeshservings.yaml b/bundle/manifests/components.platform.opendatahub.io_modelmeshservings.yaml index a9be9f4a080..28dcba7d488 100644 --- a/bundle/manifests/components.platform.opendatahub.io_modelmeshservings.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_modelmeshservings.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/components.platform.opendatahub.io_modelregistries.yaml b/bundle/manifests/components.platform.opendatahub.io_modelregistries.yaml index d23f767edc4..69b232997bd 100644 --- a/bundle/manifests/components.platform.opendatahub.io_modelregistries.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_modelregistries.yaml @@ -151,6 +151,21 @@ spec: type: string registriesNamespace: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/components.platform.opendatahub.io_rays.yaml b/bundle/manifests/components.platform.opendatahub.io_rays.yaml index e1073bed74b..c62bfe1f640 100644 --- a/bundle/manifests/components.platform.opendatahub.io_rays.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_rays.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/components.platform.opendatahub.io_trainingoperators.yaml b/bundle/manifests/components.platform.opendatahub.io_trainingoperators.yaml index 7dc395ac2b1..1f82d6bbf1c 100644 --- a/bundle/manifests/components.platform.opendatahub.io_trainingoperators.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_trainingoperators.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/components.platform.opendatahub.io_trustyais.yaml b/bundle/manifests/components.platform.opendatahub.io_trustyais.yaml index 522954961fe..53334fa31ea 100644 --- a/bundle/manifests/components.platform.opendatahub.io_trustyais.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_trustyais.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/components.platform.opendatahub.io_workbenches.yaml b/bundle/manifests/components.platform.opendatahub.io_workbenches.yaml index d75be60e4af..2668712dfa9 100644 --- a/bundle/manifests/components.platform.opendatahub.io_workbenches.yaml +++ b/bundle/manifests/components.platform.opendatahub.io_workbenches.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/bundle/manifests/datasciencecluster.opendatahub.io_datascienceclusters.yaml b/bundle/manifests/datasciencecluster.opendatahub.io_datascienceclusters.yaml index 7ec8e179cd6..d0575c145e8 100644 --- a/bundle/manifests/datasciencecluster.opendatahub.io_datascienceclusters.yaml +++ b/bundle/manifests/datasciencecluster.opendatahub.io_datascienceclusters.yaml @@ -676,6 +676,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object dashboard: description: Dashboard component status. @@ -694,6 +712,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map url: type: string type: object @@ -714,6 +750,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object kserve: description: Kserve component status. @@ -737,6 +791,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object kueue: description: Kueue component status. @@ -755,6 +827,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object modelmeshserving: description: ModelMeshServing component status. @@ -773,6 +863,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object modelregistry: description: ModelRegistry component status. @@ -793,6 +901,21 @@ spec: type: string registriesNamespace: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array type: object ray: description: Ray component status. @@ -811,6 +934,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object trainingoperator: description: Training Operator component status. @@ -829,6 +970,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object trustyai: description: TrustyAI component status. @@ -847,6 +1006,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object workbenches: description: Workbenches component status. @@ -865,6 +1042,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object conditions: diff --git a/bundle/manifests/opendatahub-operator.clusterserviceversion.yaml b/bundle/manifests/opendatahub-operator.clusterserviceversion.yaml index fa61b6093e7..5ca07be79f2 100644 --- a/bundle/manifests/opendatahub-operator.clusterserviceversion.yaml +++ b/bundle/manifests/opendatahub-operator.clusterserviceversion.yaml @@ -105,9 +105,9 @@ metadata: capabilities: Full Lifecycle categories: AI/Machine Learning, Big Data certified: "False" - containerImage: quay.io/opendatahub/opendatahub-operator:v2.21.0 - createdAt: "2025-01-02T20:06:57Z" - olm.skipRange: '>=1.0.0 <2.21.0' + containerImage: quay.io/opendatahub/opendatahub-operator:v2.23.1 + createdAt: "2025-01-28T15:13:51Z" + olm.skipRange: '>=1.0.0 <2.23.1' operators.operatorframework.io/builder: operator-sdk-v1.31.0 operators.operatorframework.io/internal-objects: '["featuretrackers.features.opendatahub.io", "codeflares.components.platform.opendatahub.io", "dashboards.components.platform.opendatahub.io", @@ -118,7 +118,7 @@ metadata: "monitorings.services.platform.opendatahub.io","modelcontrollers.components.platform.opendatahub.io"]' operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/opendatahub-io/opendatahub-operator - name: opendatahub-operator.v2.21.0 + name: opendatahub-operator.v2.18.0-versions-mapping namespace: placeholder spec: apiservicedefinitions: {} @@ -419,6 +419,17 @@ spec: - build.openshift.io resources: - buildconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - build.openshift.io + resources: - buildconfigs/instantiate - builds verbs: @@ -525,6 +536,16 @@ spec: - console.openshift.io resources: - consolelinks + verbs: + - create + - delete + - get + - list + - patch + - watch + - apiGroups: + - console.openshift.io + resources: - odhquickstarts verbs: - create @@ -532,6 +553,7 @@ spec: - get - list - patch + - update - watch - apiGroups: - controller-runtime.sigs.k8s.io @@ -648,6 +670,7 @@ spec: - get - list - patch + - update - watch - apiGroups: - datasciencecluster.opendatahub.io @@ -760,6 +783,14 @@ spec: - patch - update - watch + - apiGroups: + - features.opendatahub.io + resources: + - featuretrackers/finalizers + verbs: + - get + - patch + - update - apiGroups: - features.opendatahub.io resources: @@ -993,6 +1024,14 @@ spec: - knativeservings verbs: - '*' + - apiGroups: + - operator.knative.dev + resources: + - knativeservings/finalizers + verbs: + - get + - patch + - update - apiGroups: - operator.openshift.io resources: @@ -1282,7 +1321,7 @@ spec: value: /opt/manifests - name: ODH_PLATFORM_TYPE value: OpenDataHub - image: REPLACE_IMAGE:latest + image: quay.io/ugiordan/opendatahub-operator:v2.18.0-versions-mapping imagePullPolicy: Always livenessProbe: httpGet: @@ -1367,7 +1406,7 @@ spec: selector: matchLabels: component: opendatahub-operator - version: 2.21.0 + version: 2.18.0-versions-mapping webhookdefinitions: - admissionReviewVersions: - v1 diff --git a/config/crd/bases/components.platform.opendatahub.io_codeflares.yaml b/config/crd/bases/components.platform.opendatahub.io_codeflares.yaml index 999e5ccfdd4..c90629f45f9 100644 --- a/config/crd/bases/components.platform.opendatahub.io_codeflares.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_codeflares.yaml @@ -141,6 +141,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/components.platform.opendatahub.io_dashboards.yaml b/config/crd/bases/components.platform.opendatahub.io_dashboards.yaml index 79da59747d8..7ba8b59e9d8 100644 --- a/config/crd/bases/components.platform.opendatahub.io_dashboards.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_dashboards.yaml @@ -146,6 +146,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map url: type: string type: object diff --git a/config/crd/bases/components.platform.opendatahub.io_datasciencepipelines.yaml b/config/crd/bases/components.platform.opendatahub.io_datasciencepipelines.yaml index 495d178bb28..adb797be573 100644 --- a/config/crd/bases/components.platform.opendatahub.io_datasciencepipelines.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_datasciencepipelines.yaml @@ -144,6 +144,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/components.platform.opendatahub.io_kserves.yaml b/config/crd/bases/components.platform.opendatahub.io_kserves.yaml index c64e35fe0ee..2a9e449b875 100644 --- a/config/crd/bases/components.platform.opendatahub.io_kserves.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_kserves.yaml @@ -226,6 +226,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/components.platform.opendatahub.io_kueues.yaml b/config/crd/bases/components.platform.opendatahub.io_kueues.yaml index 1232c4dcadb..622d7f5b75c 100644 --- a/config/crd/bases/components.platform.opendatahub.io_kueues.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_kueues.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/components.platform.opendatahub.io_modelmeshservings.yaml b/config/crd/bases/components.platform.opendatahub.io_modelmeshservings.yaml index 8a8b82f876f..abebd6b7171 100644 --- a/config/crd/bases/components.platform.opendatahub.io_modelmeshservings.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_modelmeshservings.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/components.platform.opendatahub.io_modelregistries.yaml b/config/crd/bases/components.platform.opendatahub.io_modelregistries.yaml index 8f538fe5810..c00203de588 100644 --- a/config/crd/bases/components.platform.opendatahub.io_modelregistries.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_modelregistries.yaml @@ -151,6 +151,21 @@ spec: type: string registriesNamespace: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/components.platform.opendatahub.io_rays.yaml b/config/crd/bases/components.platform.opendatahub.io_rays.yaml index 92f3b3c62c3..1a8f5ba4d81 100644 --- a/config/crd/bases/components.platform.opendatahub.io_rays.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_rays.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/components.platform.opendatahub.io_trainingoperators.yaml b/config/crd/bases/components.platform.opendatahub.io_trainingoperators.yaml index 82b7262849b..7ff9d9a623f 100644 --- a/config/crd/bases/components.platform.opendatahub.io_trainingoperators.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_trainingoperators.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/components.platform.opendatahub.io_trustyais.yaml b/config/crd/bases/components.platform.opendatahub.io_trustyais.yaml index 10f42dfb24e..bdf044e4eb1 100644 --- a/config/crd/bases/components.platform.opendatahub.io_trustyais.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_trustyais.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/components.platform.opendatahub.io_workbenches.yaml b/config/crd/bases/components.platform.opendatahub.io_workbenches.yaml index e928970082d..a9273eb6d49 100644 --- a/config/crd/bases/components.platform.opendatahub.io_workbenches.yaml +++ b/config/crd/bases/components.platform.opendatahub.io_workbenches.yaml @@ -142,6 +142,24 @@ spec: type: integer phase: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed status + of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object x-kubernetes-validations: diff --git a/config/crd/bases/datasciencecluster.opendatahub.io_datascienceclusters.yaml b/config/crd/bases/datasciencecluster.opendatahub.io_datascienceclusters.yaml index 6a1c89dea45..6ea8c60e89f 100644 --- a/config/crd/bases/datasciencecluster.opendatahub.io_datascienceclusters.yaml +++ b/config/crd/bases/datasciencecluster.opendatahub.io_datascienceclusters.yaml @@ -676,6 +676,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object dashboard: description: Dashboard component status. @@ -694,6 +712,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map url: type: string type: object @@ -714,6 +750,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object kserve: description: Kserve component status. @@ -737,6 +791,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object kueue: description: Kueue component status. @@ -755,6 +827,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object modelmeshserving: description: ModelMeshServing component status. @@ -773,6 +863,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object modelregistry: description: ModelRegistry component status. @@ -793,6 +901,21 @@ spec: type: string registriesNamespace: type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array type: object ray: description: Ray component status. @@ -811,6 +934,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object trainingoperator: description: Training Operator component status. @@ -829,6 +970,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object trustyai: description: TrustyAI component status. @@ -847,6 +1006,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object workbenches: description: Workbenches component status. @@ -865,6 +1042,24 @@ spec: - Removed pattern: ^(Managed|Unmanaged|Force|Removed)$ type: string + releases: + items: + description: ComponentReleaseStatus represents the detailed + status of a component release. + properties: + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map type: object type: object conditions: diff --git a/config/manifests/bases/opendatahub-operator.clusterserviceversion.yaml b/config/manifests/bases/opendatahub-operator.clusterserviceversion.yaml index 91a28f1d034..148967aa4cc 100644 --- a/config/manifests/bases/opendatahub-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/opendatahub-operator.clusterserviceversion.yaml @@ -6,9 +6,9 @@ metadata: capabilities: Full Lifecycle categories: AI/Machine Learning, Big Data certified: "False" - containerImage: quay.io/opendatahub/opendatahub-operator:v2.21.0 - createdAt: "2024-11-18T00:00:00Z" - olm.skipRange: '>=1.0.0 <2.21.0' + containerImage: quay.io/opendatahub/opendatahub-operator:v2.23.1 + createdAt: "2025-1-23T00:00:00Z" + olm.skipRange: '>=1.0.0 <2.23.1' operators.operatorframework.io/internal-objects: '["featuretrackers.features.opendatahub.io", "codeflares.components.platform.opendatahub.io", "dashboards.components.platform.opendatahub.io", "datasciencepipelines.components.platform.opendatahub.io", "kserves.components.platform.opendatahub.io", @@ -17,7 +17,7 @@ metadata: "trainingoperators.components.platform.opendatahub.io", "trustyais.components.platform.opendatahub.io", "workbenches.components.platform.opendatahub.io", "monitorings.services.platform.opendatahub.io","modelcontrollers.components.platform.opendatahub.io"]' repository: https://github.com/opendatahub-io/opendatahub-operator - name: opendatahub-operator.v2.21.0 + name: opendatahub-operator.v2.23.1 namespace: placeholder spec: apiservicedefinitions: {} @@ -179,4 +179,4 @@ spec: selector: matchLabels: component: opendatahub-operator - version: 2.21.0 + version: 2.23.1 diff --git a/config/monitoring/networkpolicy/operator/operator.yaml b/config/monitoring/networkpolicy/operator/operator.yaml index 5c3727a8cc3..3d98e39b891 100644 --- a/config/monitoring/networkpolicy/operator/operator.yaml +++ b/config/monitoring/networkpolicy/operator/operator.yaml @@ -24,7 +24,7 @@ spec: kubernetes.io/metadata.name: openshift-console - namespaceSelector: matchLabels: - kubernetes.io/metadata.name: oopenshift-operators + kubernetes.io/metadata.name: openshift-operators - namespaceSelector: matchLabels: opendatahub.io/generated-namespace: "true" diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 06ab6910f9e..78260a3198a 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -154,6 +154,17 @@ rules: - build.openshift.io resources: - buildconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - build.openshift.io + resources: - buildconfigs/instantiate - builds verbs: @@ -260,6 +271,16 @@ rules: - console.openshift.io resources: - consolelinks + verbs: + - create + - delete + - get + - list + - patch + - watch +- apiGroups: + - console.openshift.io + resources: - odhquickstarts verbs: - create @@ -267,6 +288,7 @@ rules: - get - list - patch + - update - watch - apiGroups: - controller-runtime.sigs.k8s.io @@ -383,6 +405,7 @@ rules: - get - list - patch + - update - watch - apiGroups: - datasciencecluster.opendatahub.io @@ -495,6 +518,14 @@ rules: - patch - update - watch +- apiGroups: + - features.opendatahub.io + resources: + - featuretrackers/finalizers + verbs: + - get + - patch + - update - apiGroups: - features.opendatahub.io resources: @@ -728,6 +759,14 @@ rules: - knativeservings verbs: - '*' +- apiGroups: + - operator.knative.dev + resources: + - knativeservings/finalizers + verbs: + - get + - patch + - update - apiGroups: - operator.openshift.io resources: diff --git a/controllers/components/codeflare/codeflare_controller.go b/controllers/components/codeflare/codeflare_controller.go index f46f5effac6..6969db01d3a 100644 --- a/controllers/components/codeflare/codeflare_controller.go +++ b/controllers/components/codeflare/codeflare_controller.go @@ -30,6 +30,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" @@ -67,6 +68,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. // Add CodeFlare-specific actions WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction()). WithAction(kustomize.NewAction( kustomize.WithCache(), kustomize.WithLabel(labels.ODH.Component(LegacyComponentName), labels.True), diff --git a/controllers/components/dashboard/dashboard_controller.go b/controllers/components/dashboard/dashboard_controller.go index f4fc644a488..6bc996a48bb 100644 --- a/controllers/components/dashboard/dashboard_controller.go +++ b/controllers/components/dashboard/dashboard_controller.go @@ -33,6 +33,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" @@ -89,6 +90,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. // actions WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction()). WithAction(configureDependencies). WithAction(kustomize.NewAction( kustomize.WithCache(), diff --git a/controllers/components/datasciencepipelines/datasciencepipelines_controller.go b/controllers/components/datasciencepipelines/datasciencepipelines_controller.go index 12db851377b..e3a426ba3ad 100644 --- a/controllers/components/datasciencepipelines/datasciencepipelines_controller.go +++ b/controllers/components/datasciencepipelines/datasciencepipelines_controller.go @@ -31,6 +31,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" @@ -64,6 +65,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. WithAction(checkPreConditions). WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction()). WithAction(kustomize.NewAction( kustomize.WithCache(), kustomize.WithLabel(labels.ODH.Component(LegacyComponentName), labels.True), diff --git a/controllers/components/datasciencepipelines/datasciencepipelines_support.go b/controllers/components/datasciencepipelines/datasciencepipelines_support.go index 985721eb866..ea11c2e8726 100644 --- a/controllers/components/datasciencepipelines/datasciencepipelines_support.go +++ b/controllers/components/datasciencepipelines/datasciencepipelines_support.go @@ -24,22 +24,15 @@ const ( var ( imageParamMap = map[string]string{ - // v1 - "IMAGES_APISERVER": "RELATED_IMAGE_ODH_ML_PIPELINES_API_SERVER_IMAGE", - "IMAGES_ARTIFACT": "RELATED_IMAGE_ODH_ML_PIPELINES_ARTIFACT_MANAGER_IMAGE", - "IMAGES_PERSISTENTAGENT": "RELATED_IMAGE_ODH_ML_PIPELINES_PERSISTENCEAGENT_IMAGE", - "IMAGES_SCHEDULEDWORKFLOW": "RELATED_IMAGE_ODH_ML_PIPELINES_SCHEDULEDWORKFLOW_IMAGE", - "IMAGES_CACHE": "RELATED_IMAGE_ODH_ML_PIPELINES_CACHE_IMAGE", - "IMAGES_DSPO": "RELATED_IMAGE_ODH_DATA_SCIENCE_PIPELINES_OPERATOR_CONTROLLER_IMAGE", - // v2 - "IMAGESV2_ARGO_APISERVER": "RELATED_IMAGE_ODH_ML_PIPELINES_API_SERVER_V2_IMAGE", - "IMAGESV2_ARGO_PERSISTENCEAGENT": "RELATED_IMAGE_ODH_ML_PIPELINES_PERSISTENCEAGENT_V2_IMAGE", - "IMAGESV2_ARGO_SCHEDULEDWORKFLOW": "RELATED_IMAGE_ODH_ML_PIPELINES_SCHEDULEDWORKFLOW_V2_IMAGE", - "IMAGESV2_ARGO_ARGOEXEC": "RELATED_IMAGE_ODH_DATA_SCIENCE_PIPELINES_ARGO_ARGOEXEC_IMAGE", - "IMAGESV2_ARGO_WORKFLOWCONTROLLER": "RELATED_IMAGE_ODH_DATA_SCIENCE_PIPELINES_ARGO_WORKFLOWCONTROLLER_IMAGE", - "V2_DRIVER_IMAGE": "RELATED_IMAGE_ODH_ML_PIPELINES_DRIVER_IMAGE", - "V2_LAUNCHER_IMAGE": "RELATED_IMAGE_ODH_ML_PIPELINES_LAUNCHER_IMAGE", - "IMAGESV2_ARGO_MLMDGRPC": "RELATED_IMAGE_ODH_MLMD_GRPC_SERVER_IMAGE", + "IMAGES_DSPO": "RELATED_IMAGE_ODH_DATA_SCIENCE_PIPELINES_OPERATOR_CONTROLLER_IMAGE", + "IMAGES_APISERVER": "RELATED_IMAGE_ODH_ML_PIPELINES_API_SERVER_V2_IMAGE", + "IMAGES_PERSISTENCEAGENT": "RELATED_IMAGE_ODH_ML_PIPELINES_PERSISTENCEAGENT_V2_IMAGE", + "IMAGES_SCHEDULEDWORKFLOW": "RELATED_IMAGE_ODH_ML_PIPELINES_SCHEDULEDWORKFLOW_V2_IMAGE", + "IMAGES_ARGO_EXEC": "RELATED_IMAGE_ODH_DATA_SCIENCE_PIPELINES_ARGO_ARGOEXEC_IMAGE", + "IMAGES_ARGO_WORKFLOWCONTROLLER": "RELATED_IMAGE_ODH_DATA_SCIENCE_PIPELINES_ARGO_WORKFLOWCONTROLLER_IMAGE", + "IMAGES_DRIVER": "RELATED_IMAGE_ODH_ML_PIPELINES_DRIVER_IMAGE", + "IMAGES_LAUNCHER": "RELATED_IMAGE_ODH_ML_PIPELINES_LAUNCHER_IMAGE", + "IMAGES_MLMDGRPC": "RELATED_IMAGE_ODH_MLMD_GRPC_SERVER_IMAGE", } overlaysSourcePaths = map[cluster.Platform]string{ diff --git a/controllers/components/kserve/kserve_controller.go b/controllers/components/kserve/kserve_controller.go index c5a62beed3a..4202b4599cb 100644 --- a/controllers/components/kserve/kserve_controller.go +++ b/controllers/components/kserve/kserve_controller.go @@ -28,19 +28,23 @@ import ( rbacv1 "k8s.io/api/rbac/v1" extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1" + dsciv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/dscinitialization/v1" featuresv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/features/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/clusterrole" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/generation" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/hash" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/resources" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/reconciler" @@ -65,17 +69,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. // changes. The compareHashPredicate ensures that we don't needlessly enqueue // requests if there are no changes that we don't care about. Owns(&templatev1.Template{}, reconciler.WithPredicates(hash.Updated())). - // The FeatureTrackers are created slightly differently, and have - // ownerRefs set by controllerutil.SetOwnerReference() rather than - // controllerutil.SetControllerReference(), which means that the default - // eventHandler for Owns won't work, so a slightly modified variant is - // added here - Owns(&featuresv1.FeatureTracker{}, reconciler.WithEventHandler( - handler.EnqueueRequestForOwner( - mgr.GetScheme(), - mgr.GetRESTMapper(), - &componentApi.Kserve{}, - ))). + Owns(&featuresv1.FeatureTracker{}). Owns(&networkingv1.NetworkPolicy{}). Owns(&monitoringv1.ServiceMonitor{}). Owns(&admissionregistrationv1.MutatingWebhookConfiguration{}). @@ -93,10 +87,30 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. &extv1.CustomResourceDefinition{}, reconciler.WithEventHandler( handlers.ToNamed(componentApi.KserveInstanceName)), - reconciler.WithPredicates( - component.ForLabel(labels.ODH.Component(LegacyComponentName), labels.True)), + reconciler.WithPredicates(predicate.And( + component.ForLabel(labels.ODH.Component(LegacyComponentName), labels.True), + predicate.Funcs{ + UpdateFunc: func(event event.UpdateEvent) bool { + // The KServe and ModelMesh are shipping the same CRDs as part of their manifests + // but with different versions, this cause the respective component reconcilers to + // keep trying to install their respective version, ending in an infinite loop. + switch event.ObjectNew.GetName() { + case "inferenceservices.serving.kserve.io": + return false + case "servingruntimes.serving.kserve.io": + return false + } + return true + }, + }, + )), + ). + // resource + Watches( + &dsciv1.DSCInitialization{}, + reconciler.WithEventHandler(handlers.ToNamed(componentApi.KserveInstanceName)), + reconciler.WithPredicates(predicate.Or(generation.New(), resources.DSCIReadiness)), ). - // operands - dynamically watched // // A watch will be created dynamically for these kinds, if they exist on the cluster @@ -138,6 +152,8 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. WithAction(checkPreConditions). WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction()). + WithAction(removeLegacyFeatureTrackerOwnerRef). WithAction(configureServerless). WithAction(configureServiceMesh). WithAction(kustomize.NewAction( diff --git a/controllers/components/kserve/kserve_controller_actions.go b/controllers/components/kserve/kserve_controller_actions.go index b12562a0f87..d7fbc4f9b16 100644 --- a/controllers/components/kserve/kserve_controller_actions.go +++ b/controllers/components/kserve/kserve_controller_actions.go @@ -9,11 +9,14 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + k8serr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1" + featuresv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/features/v1" "github.com/opendatahub-io/opendatahub-operator/v2/controllers/status" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk" @@ -22,6 +25,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/feature" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/resources" ) func checkPreConditions(ctx context.Context, rr *odhtypes.ReconciliationRequest) error { @@ -137,6 +141,32 @@ func devFlags(ctx context.Context, rr *odhtypes.ReconciliationRequest) error { return nil } +func removeLegacyFeatureTrackerOwnerRef(ctx context.Context, rr *odhtypes.ReconciliationRequest) error { + ftNames := []string{ + rr.DSCI.Spec.ApplicationsNamespace + "-serverless-serving-deployment", + rr.DSCI.Spec.ApplicationsNamespace + "-serverless-net-istio-secret-filtering", + rr.DSCI.Spec.ApplicationsNamespace + "-serverless-serving-gateways", + rr.DSCI.Spec.ApplicationsNamespace + "-kserve-external-authz", + } + + for _, ftName := range ftNames { + obj := &featuresv1.FeatureTracker{} + err := rr.Client.Get(ctx, client.ObjectKey{Name: ftName}, obj) + switch { + case k8serr.IsNotFound(err): + continue + case err != nil: + return fmt.Errorf("error while retrieving FeatureTracker %s: %w", ftName, err) + } + + if err := resources.RemoveOwnerReferences(ctx, rr.Client, obj, isLegacyOwnerRef); err != nil { + return err + } + } + + return nil +} + func configureServerless(ctx context.Context, rr *odhtypes.ReconciliationRequest) error { k, ok := rr.Instance.(*componentApi.Kserve) if !ok { diff --git a/controllers/components/kserve/kserve_support.go b/controllers/components/kserve/kserve_support.go index 2f12314db33..528cdbdceea 100644 --- a/controllers/components/kserve/kserve_support.go +++ b/controllers/components/kserve/kserve_support.go @@ -22,6 +22,7 @@ import ( dsciv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/dscinitialization/v1" featuresv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/features/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk" odhtypes "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/types" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/feature" @@ -266,3 +267,7 @@ func ownedViaFT(cli client.Client) handler.MapFunc { return []reconcile.Request{} } } + +func isLegacyOwnerRef(or metav1.OwnerReference) bool { + return or.APIVersion == gvk.DataScienceCluster.GroupVersion().String() && or.Kind == gvk.DataScienceCluster.Kind +} diff --git a/controllers/components/kueue/kueue_controller.go b/controllers/components/kueue/kueue_controller.go index d9ec73c56de..5566fef2e72 100644 --- a/controllers/components/kueue/kueue_controller.go +++ b/controllers/components/kueue/kueue_controller.go @@ -35,6 +35,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" @@ -76,6 +77,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. // Add Kueue-specific actions WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction()). WithAction(kustomize.NewAction( kustomize.WithCache(), kustomize.WithLabel(labels.ODH.Component(LegacyComponentName), labels.True), diff --git a/controllers/components/modelmeshserving/modelmeshserving_controller.go b/controllers/components/modelmeshserving/modelmeshserving_controller.go index d3c4a674558..516f7466319 100644 --- a/controllers/components/modelmeshserving/modelmeshserving_controller.go +++ b/controllers/components/modelmeshserving/modelmeshserving_controller.go @@ -27,11 +27,14 @@ import ( rbacv1 "k8s.io/api/rbac/v1" extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/clusterrole" @@ -62,12 +65,28 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. &extv1.CustomResourceDefinition{}, reconciler.WithEventHandler( handlers.ToNamed(componentApi.ModelMeshServingInstanceName)), - reconciler.WithPredicates( - component.ForLabel(labels.ODH.Component(LegacyComponentName), labels.True)), + reconciler.WithPredicates(predicate.And( + component.ForLabel(labels.ODH.Component(LegacyComponentName), labels.True), + predicate.Funcs{ + UpdateFunc: func(event event.UpdateEvent) bool { + // The KServe and ModelMesh are shipping the same CRDs as part of their manifests + // but with different versions, this cause the respective component reconcilers to + // keep trying to install their respective version, ending in an infinite loop. + switch event.ObjectNew.GetName() { + case "inferenceservices.serving.kserve.io": + return false + case "servingruntimes.serving.kserve.io": + return false + } + return true + }, + }, + )), ). // Add ModelMeshServing specific actions WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction()). WithAction(kustomize.NewAction( kustomize.WithCache(), kustomize.WithLabel(labels.ODH.Component(LegacyComponentName), labels.True), diff --git a/controllers/components/modelregistry/modelregistry_controller.go b/controllers/components/modelregistry/modelregistry_controller.go index 800ef932028..9ae46c52ee8 100644 --- a/controllers/components/modelregistry/modelregistry_controller.go +++ b/controllers/components/modelregistry/modelregistry_controller.go @@ -34,6 +34,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/template" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" @@ -82,6 +83,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. // actions WithAction(checkPreConditions). WithAction(initialize). + WithAction(releases.NewAction()). WithAction(configureDependencies). WithAction(template.NewAction( template.WithCache(), diff --git a/controllers/components/ray/ray_controller.go b/controllers/components/ray/ray_controller.go index adc4ad65271..48eca16ad83 100644 --- a/controllers/components/ray/ray_controller.go +++ b/controllers/components/ray/ray_controller.go @@ -30,6 +30,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" @@ -60,6 +61,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. // Add Ray-specific actions WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction()). WithAction(kustomize.NewAction( kustomize.WithCache(), kustomize.WithLabel(labels.ODH.Component(LegacyComponentName), labels.True), diff --git a/controllers/components/trainingoperator/trainingoperator_controller.go b/controllers/components/trainingoperator/trainingoperator_controller.go index 096ef8a5198..6e5ceecfd10 100644 --- a/controllers/components/trainingoperator/trainingoperator_controller.go +++ b/controllers/components/trainingoperator/trainingoperator_controller.go @@ -30,6 +30,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" @@ -57,6 +58,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. // Add TrainingOperator-specific actions WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction()). WithAction(kustomize.NewAction( kustomize.WithCache(), kustomize.WithLabel(labels.ODH.Component(LegacyComponentName), labels.True), diff --git a/controllers/components/trustyai/trustyai_controller.go b/controllers/components/trustyai/trustyai_controller.go index e77bdd151de..32147fc9993 100644 --- a/controllers/components/trustyai/trustyai_controller.go +++ b/controllers/components/trustyai/trustyai_controller.go @@ -29,6 +29,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" @@ -58,6 +59,7 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. // Add TrustyAI-specific actions WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction()). WithAction(kustomize.NewAction( kustomize.WithCache(), kustomize.WithLabel(labels.ODH.Component(LegacyComponentName), labels.True), diff --git a/controllers/components/workbenches/workbenches_controller.go b/controllers/components/workbenches/workbenches_controller.go index 0a790c2ab81..f63238893bc 100644 --- a/controllers/components/workbenches/workbenches_controller.go +++ b/controllers/components/workbenches/workbenches_controller.go @@ -18,6 +18,7 @@ package workbenches import ( "context" + "path" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" appsv1 "k8s.io/api/apps/v1" @@ -30,11 +31,13 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/gc" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/render/kustomize" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/updatestatus" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/handlers" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/component" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/predicates/resources" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/reconciler" + odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels" ) @@ -60,6 +63,9 @@ func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl. ). WithAction(initialize). WithAction(devFlags). + WithAction(releases.NewAction( + releases.WithMetadataFilePath( + path.Join(odhdeploy.DefaultManifestPath, ComponentName, kfNotebookControllerPath, releases.ComponentMetadataFilename)))). WithAction(configureDependencies). WithAction(kustomize.NewAction( kustomize.WithCache(), diff --git a/controllers/datasciencecluster/kubebuilder_rbac.go b/controllers/datasciencecluster/kubebuilder_rbac.go index 86f4956a1d2..779d069e893 100644 --- a/controllers/datasciencecluster/kubebuilder_rbac.go +++ b/controllers/datasciencecluster/kubebuilder_rbac.go @@ -136,10 +136,10 @@ package datasciencecluster // +kubebuilder:rbac:groups=components.platform.opendatahub.io,resources=dashboards/status,verbs=get;update;patch // +kubebuilder:rbac:groups=components.platform.opendatahub.io,resources=dashboards/finalizers,verbs=create;get;list;patch;update;use;watch // +kubebuilder:rbac:groups="opendatahub.io",resources=odhdashboardconfigs,verbs=create;get;patch;watch;update;delete;list -// +kubebuilder:rbac:groups="console.openshift.io",resources=odhquickstarts,verbs=create;get;patch;list;delete;watch -// +kubebuilder:rbac:groups="dashboard.opendatahub.io",resources=odhdocuments,verbs=create;get;patch;list;delete;watch -// +kubebuilder:rbac:groups="dashboard.opendatahub.io",resources=odhapplications,verbs=create;get;patch;list;delete;watch -// +kubebuilder:rbac:groups="dashboard.opendatahub.io",resources=acceleratorprofiles,verbs=create;get;patch;list;delete;watch +// +kubebuilder:rbac:groups="console.openshift.io",resources=odhquickstarts,verbs=create;get;patch;list;delete;watch;update +// +kubebuilder:rbac:groups="dashboard.opendatahub.io",resources=odhdocuments,verbs=create;get;patch;list;delete;watch;update +// +kubebuilder:rbac:groups="dashboard.opendatahub.io",resources=odhapplications,verbs=create;get;patch;list;delete;watch;update +// +kubebuilder:rbac:groups="dashboard.opendatahub.io",resources=acceleratorprofiles,verbs=create;get;patch;list;delete;watch;update // ModelRegistry // +kubebuilder:rbac:groups=components.platform.opendatahub.io,resources=modelregistries,verbs=get;list;watch;create;update;patch;delete @@ -191,6 +191,7 @@ package datasciencecluster /* Serverless prerequisite */ // +kubebuilder:rbac:groups="networking.istio.io",resources=gateways,verbs=* // +kubebuilder:rbac:groups="operator.knative.dev",resources=knativeservings,verbs=* +// +kubebuilder:rbac:groups="operator.knative.dev",resources=knativeservings/finalizers,verbs=update;patch;get // +kubebuilder:rbac:groups="config.openshift.io",resources=ingresses,verbs=get // WB @@ -203,7 +204,7 @@ package datasciencecluster // OpenVino still need buildconfig // +kubebuilder:rbac:groups="build.openshift.io",resources=builds,verbs=create;patch;delete;list;watch;get // +kubebuilder:rbac:groups="build.openshift.io",resources=buildconfigs/instantiate,verbs=create;patch;delete;get;list;watch -// +kubebuilder:rbac:groups="build.openshift.io",resources=buildconfigs,verbs=list;watch;create;patch;delete;get +// +kubebuilder:rbac:groups="build.openshift.io",resources=buildconfigs,verbs=list;watch;create;patch;delete;get;update // DataSciencePipelines // +kubebuilder:rbac:groups=components.platform.opendatahub.io,resources=datasciencepipelines,verbs=get;list;watch;create;update;patch;delete diff --git a/controllers/dscinitialization/dscinitialization_controller.go b/controllers/dscinitialization/dscinitialization_controller.go index 042a5506cc5..eaacbd4aa08 100644 --- a/controllers/dscinitialization/dscinitialization_controller.go +++ b/controllers/dscinitialization/dscinitialization_controller.go @@ -67,9 +67,8 @@ var managementStateChangeTrustedCA = false // DSCInitializationReconciler reconciles a DSCInitialization object. type DSCInitializationReconciler struct { *odhClient.Client - Scheme *runtime.Scheme - Recorder record.EventRecorder - ApplicationsNamespace string + Scheme *runtime.Scheme + Recorder record.EventRecorder } // Reconcile contains controller logic specific to DSCInitialization instance updates. @@ -249,7 +248,7 @@ func (r *DSCInitializationReconciler) Reconcile(ctx context.Context, req ctrl.Re } case cluster.ManagedRhoai: osdConfigsPath := filepath.Join(deploy.DefaultManifestPath, "osd-configs") - err = deploy.DeployManifestsFromPath(ctx, r.Client, instance, osdConfigsPath, r.ApplicationsNamespace, "osd", true) + err = deploy.DeployManifestsFromPath(ctx, r.Client, instance, osdConfigsPath, instance.Spec.ApplicationsNamespace, "osd", true) if err != nil { log.Error(err, "Failed to apply osd specific configs from manifests", "Manifests path", osdConfigsPath) r.Recorder.Eventf(instance, corev1.EventTypeWarning, "DSCInitializationReconcileError", "Failed to apply "+osdConfigsPath) @@ -445,7 +444,7 @@ func (r *DSCInitializationReconciler) watchAuthResource(ctx context.Context, a c if len(instanceList.Items) == 0 { log.Info("Found no Auth instance in cluster, reconciling to recreate") - return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: "auth", Namespace: r.ApplicationsNamespace}}} + return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: "auth"}}} } return nil diff --git a/controllers/dscinitialization/kubebuilder_rbac.go b/controllers/dscinitialization/kubebuilder_rbac.go index 7f5ac8d662e..dde5fbac446 100644 --- a/controllers/dscinitialization/kubebuilder_rbac.go +++ b/controllers/dscinitialization/kubebuilder_rbac.go @@ -5,6 +5,7 @@ package dscinitialization // +kubebuilder:rbac:groups="dscinitialization.opendatahub.io",resources=dscinitializations,verbs=get;list;watch;create;update;patch;delete;deletecollection // +kubebuilder:rbac:groups="features.opendatahub.io",resources=featuretrackers,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="features.opendatahub.io",resources=featuretrackers/status,verbs=get;update;patch;delete +// +kubebuilder:rbac:groups="features.opendatahub.io",resources=featuretrackers/finalizers,verbs=update;patch;get /* Auth */ // +kubebuilder:rbac:groups="config.openshift.io",resources=authentications,verbs=get;watch;list diff --git a/controllers/dscinitialization/monitoring.go b/controllers/dscinitialization/monitoring.go index f3f0df71636..857b4206ce2 100644 --- a/controllers/dscinitialization/monitoring.go +++ b/controllers/dscinitialization/monitoring.go @@ -29,8 +29,6 @@ var ( prometheusManifestsPath = filepath.Join(deploy.DefaultManifestPath, ComponentName, "prometheus", "base") prometheusConfigPath = filepath.Join(deploy.DefaultManifestPath, ComponentName, "prometheus", "apps") networkpolicyPath = filepath.Join(deploy.DefaultManifestPath, ComponentName, "networkpolicy") - NameConsoleLink = "console" - NamespaceConsoleLink = "openshift-console" ) // only when reconcile on DSCI CR, initial set to true @@ -349,7 +347,7 @@ func configurePrometheus(ctx context.Context, dsciInit *dsciv1.DSCInitialization func configureBlackboxExporter(ctx context.Context, dsciInit *dsciv1.DSCInitialization, r *DSCInitializationReconciler) error { log := logf.FromContext(ctx) consoleRoute := &routev1.Route{} - err := r.Client.Get(ctx, client.ObjectKey{Name: "console", Namespace: "openshift-console"}, consoleRoute) + err := r.Client.Get(ctx, client.ObjectKey{Name: cluster.NameConsoleLink, Namespace: cluster.NamespaceConsoleLink}, consoleRoute) if err != nil { if !k8serr.IsNotFound(err) { return err diff --git a/controllers/dscinitialization/utils.go b/controllers/dscinitialization/utils.go index 87fe3c37097..5c00a5f4215 100644 --- a/controllers/dscinitialization/utils.go +++ b/controllers/dscinitialization/utils.go @@ -51,7 +51,7 @@ func (r *DSCInitializationReconciler) createOperatorResource(ctx context.Context return err } - // Patch monitoring namespace + // Patch monitoring namespace: no difference for any type of platform err := r.patchMonitoringNS(ctx, dscInit) if err != nil { log.Error(err, "error patch monitoring namespace") @@ -125,7 +125,7 @@ func (r *DSCInitializationReconciler) createAppNamespace(ctx context.Context, ns // label only for managed cluster if platform == cluster.ManagedRhoai { - labelList["openshift.io/cluster-monitoring"] = "true" + labelList[labels.ClusterMonitoring] = labels.True } for _, l := range extraLabel { @@ -153,9 +153,9 @@ func (r *DSCInitializationReconciler) patchMonitoringNS(ctx context.Context, dsc ObjectMeta: metav1.ObjectMeta{ Name: monitoringName, Labels: map[string]string{ - labels.ODH.OwnedNamespace: "true", + labels.ODH.OwnedNamespace: labels.True, labels.SecurityEnforce: "baseline", - labels.ClusterMonitoring: "true", + labels.ClusterMonitoring: labels.True, }, }, } @@ -233,6 +233,17 @@ func (r *DSCInitializationReconciler) reconcileDefaultNetworkPolicy( }, }, }, + { // OR logic to minic customized application namespace + From: []networkingv1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ // AND logic + MatchLabels: map[string]string{ + labels.CustomizedAppNamespace: labels.True, + }, + }, + }, + }, + }, { // OR logic From: []networkingv1.NetworkPolicyPeer{ { // need this to access external-> dashboard diff --git a/controllers/services/auth/resources/README.md b/controllers/services/auth/resources/README.md new file mode 100644 index 00000000000..d04c710e697 --- /dev/null +++ b/controllers/services/auth/resources/README.md @@ -0,0 +1,27 @@ +# Permissions For Users + +When adding permissions for users, it's best to consider the "[reasonable level](#reasonable-level)" of permissions. + +## Reasonable Level + +> **Note:** This is a recommendation -- a good starting spot -- not a hard-fast rule. + +When granting access to a bot (like a service account) you'd aim to grant specific actions the bot would directly do -- need patch? give patch; need read? give read. However, when granting permissions to users, you often want to take a wider approach -- need patch? give update & patch; need read? give get, list, and watch. + +Admins should be considered as people with a desire of a clean user experience, and not a means to an end to complete a development flow. From there, we can look at if it is appropriate for us to be granting such wide permissions. + +Consider the CRUD actions an admin or allowed user gets... as a user is granted access over resources, there a logical good UX extension to grant them inverse or complementary access. Consider the following CRUD layout as it is related to each k8s verbs: +* C - Create +* R - Get, List, Watch +* U - Update, Patch +* D - Delete + +Furthermore, when a user gets “C” (create) actions... we should consider them having the ability to delete them, as to clean up their mistakes. Naturally they’ll need read permissions too so they can interact with them. The mapping looks something like this: +* Need **C** => Gets **CRUD** +* Need **R** => Gets **R** +* Need **U** => Gets **RU** +* Need **D** => Gets **CRUD** + +The guidance would be for you to start with the idea that if you need one of the CRUD actions, you get the corresponding CRUD relationship. It’s all additive. If you need just “R” – you only get to read (which boils down to get, list, & watch powers). If you need Create? You’d look to get the full CRUD gambit. + +It’s important to note, this is a guidance and recommendation for the user experience of the admin behind the permissions. Not a hard-fast rule. Consider the ramifications of broadly granting access to the resource in question you’re looking at. diff --git a/controllers/services/auth/resources/admingroup-clusterrole.tmpl.yaml b/controllers/services/auth/resources/admingroup-clusterrole.tmpl.yaml index 402673cbd56..4ab79189b4c 100644 --- a/controllers/services/auth/resources/admingroup-clusterrole.tmpl.yaml +++ b/controllers/services/auth/resources/admingroup-clusterrole.tmpl.yaml @@ -46,15 +46,3 @@ rules: - get - list - watch -- apiGroups: - - nim.opendatahub.io - resources: - - accounts - verbs: - - watch - - update - - get - - list - - create - - patch - - delete diff --git a/controllers/services/auth/resources/admingroup-role.tmpl.yaml b/controllers/services/auth/resources/admingroup-role.tmpl.yaml index fce1ef797ca..a87f9e805b6 100644 --- a/controllers/services/auth/resources/admingroup-role.tmpl.yaml +++ b/controllers/services/auth/resources/admingroup-role.tmpl.yaml @@ -13,6 +13,7 @@ rules: - list - watch - patch + - update - apiGroups: - services.opendatahub.io resources: @@ -23,11 +24,14 @@ rules: - dashboard.opendatahub.io resources: - acceleratorprofiles + - hardwareprofiles verbs: - create - get - list + - watch - patch + - update - delete - apiGroups: - route.openshift.io @@ -54,6 +58,7 @@ rules: - get - list - patch + - update - delete - watch - apiGroups: @@ -63,6 +68,8 @@ rules: - buildconfigs verbs: - list + - get + - watch - apiGroups: - apps resources: @@ -76,6 +83,7 @@ rules: - odhdashboardconfigs verbs: - get + - list - watch - create - update @@ -96,7 +104,8 @@ rules: - odhdocuments verbs: - get - - list + - list + - watch - apiGroups: - console.openshift.io resources: @@ -104,6 +113,7 @@ rules: verbs: - get - list + - watch - apiGroups: - template.openshift.io resources: @@ -114,6 +124,7 @@ rules: - watch - create - patch + - update - delete - apiGroups: - serving.kserve.io @@ -121,3 +132,15 @@ rules: - servingruntimes verbs: - create +- apiGroups: + - nim.opendatahub.io + resources: + - accounts + verbs: + - watch + - update + - get + - list + - create + - patch + - delete diff --git a/controllers/services/monitoring/monitoring.go b/controllers/services/monitoring/monitoring.go index aac138dc2d1..ab48f583db6 100644 --- a/controllers/services/monitoring/monitoring.go +++ b/controllers/services/monitoring/monitoring.go @@ -7,7 +7,6 @@ import ( "path/filepath" "strings" - conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1" "gopkg.in/yaml.v2" k8serr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -24,7 +23,6 @@ import ( var ( ComponentName = serviceApi.MonitoringServiceName prometheusConfigPath = filepath.Join(odhdeploy.DefaultManifestPath, ComponentName, "prometheus", "apps", "prometheus-configs.yaml") - ReadyConditionType = conditionsv1.ConditionType(status.ReadySuffix) ) // updatePrometheusConfig update prometheus-configs.yaml to include/exclude .rules diff --git a/controllers/services/monitoring/monitoring_controller_actions.go b/controllers/services/monitoring/monitoring_controller_actions.go index 03b6bd43942..a87e939706e 100644 --- a/controllers/services/monitoring/monitoring_controller_actions.go +++ b/controllers/services/monitoring/monitoring_controller_actions.go @@ -10,13 +10,11 @@ import ( "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" - logf "sigs.k8s.io/controller-runtime/pkg/log" componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" serviceApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/services/v1alpha1" "github.com/opendatahub-io/opendatahub-operator/v2/controllers/status" - "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" cr "github.com/opendatahub-io/opendatahub-operator/v2/pkg/componentsregistry" odhtypes "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/types" odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" @@ -38,22 +36,13 @@ var componentRules = map[string]string{ } // initialize handles all pre-deployment configurations. -func initialize(ctx context.Context, rr *odhtypes.ReconciliationRequest) error { - log := logf.FromContext(ctx) - // Only handle manifests setup and initial configurations - platform := rr.Release.Name - switch platform { - case cluster.ManagedRhoai: - // Only set prometheus configmap path - rr.Manifests = []odhtypes.ManifestInfo{ - { - Path: odhdeploy.DefaultManifestPath, - ContextDir: "monitoring/prometheus/apps", - }, - } - - default: - log.V(3).Info("Monitoring enabled, won't apply changes in this mode", "cluster", platform) +func initialize(_ context.Context, rr *odhtypes.ReconciliationRequest) error { + // Only set prometheus configmap path + rr.Manifests = []odhtypes.ManifestInfo{ + { + Path: odhdeploy.DefaultManifestPath, + ContextDir: "monitoring/prometheus/apps", + }, } return nil @@ -106,10 +95,13 @@ func updateStatus(ctx context.Context, rr *odhtypes.ReconciliationRequest) error } // TODO: deprecate phase + // Cannot use status.PhaseNotReady as the value here is not the + // same as the constant ("Not Ready") m.Status.Phase = "NotReady" + // condition nc := metav1.Condition{ - Type: string(ReadyConditionType), + Type: status.ConditionTypeReady, Status: metav1.ConditionFalse, Reason: status.PhaseNotReady, Message: "Prometheus deployment is not ready", @@ -119,7 +111,7 @@ func updateStatus(ctx context.Context, rr *odhtypes.ReconciliationRequest) error err := rr.Client.List( ctx, promDeployment, - client.InNamespace(rr.DSCI.Spec.Monitoring.Namespace), + client.InNamespace(m.Spec.Namespace), ) if err != nil { return fmt.Errorf("error fetching promethus deployments: %w", err) @@ -134,7 +126,7 @@ func updateStatus(ctx context.Context, rr *odhtypes.ReconciliationRequest) error if len(promDeployment.Items) == ready { // TODO: deprecate phase - m.Status.Phase = "Ready" + m.Status.Phase = status.PhaseReady // condition nc.Status = metav1.ConditionTrue nc.Reason = status.ReconcileCompleted diff --git a/docs/COMPONENT_INTEGRATION.md b/docs/COMPONENT_INTEGRATION.md index e129b4383dd..f5605e05b20 100644 --- a/docs/COMPONENT_INTEGRATION.md +++ b/docs/COMPONENT_INTEGRATION.md @@ -220,7 +220,7 @@ This function will be responsible for creating the reconciler for the previously - resource ownership - using `.Owns()` - watching a resource - using `.Watches()` - reconciler actions - using `.WithAction()` - - this includes pre-implemented actions used commonly across components (e.g. manifest rendering), as well as custom, component-specific actions + - this includes pre-implemented actions used commonly across components (e.g. manifests rendering), as well as customized, component-specific actions - more details on actions are provided [below](#actions) The example pseudo-implementation should look like as follows: @@ -261,7 +261,6 @@ As seen in the existing component reconciler implementations, it would be recomm In addition, proper generic actions, intended to be used across the components, are provided as part of the operator implementation (located in `pkg/controller/actions`). These support: -- (if necessary) creating pod security role binding - manifest rendering - can additionally utilize caching - manifest deployment @@ -274,13 +273,9 @@ If the new component requires additional custom logic, custom actions can also b For practical examples of all the above-mentioned functionality, please refer to the implementations within `controllers/components` directory. -#### Update upgrade.go - -Update the `CreateDefaultDSC()` function in `pkg/upgrade/upgrade.go` to include the newly added component. - #### Update main.go -Add an import for the the newly added component: +Import the newly added component: ```diff package main @@ -302,24 +297,32 @@ the e2e test suite to capture deployments introduced by the new component. Existing e2e test suites for the integrated components can be also found there. Lastly, please update the following files to fully integrate new component tests into the overall test suite: -- update `setupDSCInstance()` function in `tests/e2e/helper_test.go` to include the newly added component -- update `newDSC()` function in `controllers/webhook/webhook_suite_test.go` to include the newly added component +- update `setupDSCInstance()` function in `tests/e2e/helper_test.go` to set new component in DSC +- update `newDSC()` function in `controllers/webhook/webhook_suite_test.go` to update creation of DSC include the new component - update `componentsTestSuites` map in `tests/e2e/controller_test.go` to include the reference for the new component e2e test suite +### 4. Update Prometheus config and tests + +If the component is planned to be released for downstream, Prometheus rules and promtest need to be updated for the component. +- Rules are located in `config/monitoring/prometheus/app/prometheus-configs.yaml` file +- Tests are grouped in `tests/prometheus_unit_tests` _unit_tests.yam file + + ## Integrated components Currently integrated components are: -- [Dashboard](https://github.com/opendatahub-io/odh-dashboard) + - [Codeflare](https://github.com/opendatahub-io/codeflare-operator) -- [Ray](https://github.com/opendatahub-io/kuberay) +- [Dashboard](https://github.com/opendatahub-io/odh-dashboard) - [Data Science Pipelines](https://github.com/opendatahub-io/data-science-pipelines) - [KServe](https://github.com/opendatahub-io/kserve) +- [Kueue](https://github.com/opendatahub-io/kueue) - [ModelMesh Serving](https://github.com/opendatahub-io/modelmesh-serving) -- [Workbenches](https://github.com/opendatahub-io/notebooks) -- [TrustyAI](https://github.com/opendatahub-io/trustyai-service-operator) +- [Model Controller](https://github.com/opendatahub-io/odh-model-controller) - [ModelRegistry](https://github.com/opendatahub-io/model-registry) -- [Kueue](https://github.com/opendatahub-io/kueue) +- [Ray](https://github.com/opendatahub-io/kuberay) - [Training Operator](https://github.com/opendatahub-io/training-operator) -- [Model Controller](https://github.com/opendatahub-io/odh-model-controller) +- [TrustyAI](https://github.com/opendatahub-io/trustyai-service-operator) +- [Workbenches](https://github.com/opendatahub-io/notebooks) The particular controller implementations for the listed components are located in the `controllers/components` directory and the corresponding internal component APIs are located in `apis/components/v1alpha1`. diff --git a/docs/api-overview.md b/docs/api-overview.md index b791e5b9788..53e39b5cf8e 100644 --- a/docs/api-overview.md +++ b/docs/api-overview.md @@ -90,6 +90,9 @@ _Appears in:_ - [CodeFlareStatus](#codeflarestatus) - [DSCCodeFlareStatus](#dsccodeflarestatus) +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `releases` _ComponentReleaseStatus array_ | | | | #### CodeFlareList @@ -144,6 +147,7 @@ _Appears in:_ | `phase` _string_ | | | | | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### DSCCodeFlare @@ -567,6 +571,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `url` _string_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### DashboardList @@ -622,6 +627,7 @@ _Appears in:_ | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | | `url` _string_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### DataSciencePipelines @@ -675,6 +681,9 @@ _Appears in:_ - [DSCDataSciencePipelinesStatus](#dscdatasciencepipelinesstatus) - [DataSciencePipelinesStatus](#datasciencepipelinesstatus) +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `releases` _ComponentReleaseStatus array_ | | | | #### DataSciencePipelinesList @@ -729,6 +738,7 @@ _Appears in:_ | `phase` _string_ | | | | | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### DefaultDeploymentMode @@ -808,6 +818,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `defaultDeploymentMode` _string_ | DefaultDeploymentMode is the value of the defaultDeploymentMode field
as read from the "deploy" JSON in the inferenceservice-config ConfigMap | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### KserveList @@ -866,6 +877,7 @@ _Appears in:_ | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | | `defaultDeploymentMode` _string_ | DefaultDeploymentMode is the value of the defaultDeploymentMode field
as read from the "deploy" JSON in the inferenceservice-config ConfigMap | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### Kueue @@ -919,6 +931,9 @@ _Appears in:_ - [DSCKueueStatus](#dsckueuestatus) - [KueueStatus](#kueuestatus) +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `releases` _ComponentReleaseStatus array_ | | | | #### KueueList @@ -973,6 +988,7 @@ _Appears in:_ | `phase` _string_ | | | | | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### ModelController @@ -1138,6 +1154,9 @@ _Appears in:_ - [DSCModelMeshServingStatus](#dscmodelmeshservingstatus) - [ModelMeshServingStatus](#modelmeshservingstatus) +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `releases` _ComponentReleaseStatus array_ | | | | #### ModelMeshServingList @@ -1192,6 +1211,7 @@ _Appears in:_ | `phase` _string_ | | | | | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### ModelRegistry @@ -1249,6 +1269,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `registriesNamespace` _string_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### ModelRegistryList @@ -1305,6 +1326,7 @@ _Appears in:_ | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | | `registriesNamespace` _string_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### NimSpec @@ -1377,6 +1399,9 @@ _Appears in:_ - [DSCRayStatus](#dscraystatus) - [RayStatus](#raystatus) +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `releases` _ComponentReleaseStatus array_ | | | | #### RayList @@ -1431,6 +1456,7 @@ _Appears in:_ | `phase` _string_ | | | | | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### TrainingOperator @@ -1484,6 +1510,9 @@ _Appears in:_ - [DSCTrainingOperatorStatus](#dsctrainingoperatorstatus) - [TrainingOperatorStatus](#trainingoperatorstatus) +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `releases` _ComponentReleaseStatus array_ | | | | #### TrainingOperatorList @@ -1538,6 +1567,7 @@ _Appears in:_ | `phase` _string_ | | | | | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### TrustyAI @@ -1591,6 +1621,9 @@ _Appears in:_ - [DSCTrustyAIStatus](#dsctrustyaistatus) - [TrustyAIStatus](#trustyaistatus) +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `releases` _ComponentReleaseStatus array_ | | | | #### TrustyAIList @@ -1645,6 +1678,7 @@ _Appears in:_ | `phase` _string_ | | | | | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | #### Workbenches @@ -1698,6 +1732,9 @@ _Appears in:_ - [DSCWorkbenchesStatus](#dscworkbenchesstatus) - [WorkbenchesStatus](#workbenchesstatus) +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `releases` _ComponentReleaseStatus array_ | | | | #### WorkbenchesList @@ -1752,6 +1789,7 @@ _Appears in:_ | `phase` _string_ | | | | | `observedGeneration` _integer_ | | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#condition-v1-meta) array_ | | | | +| `releases` _ComponentReleaseStatus array_ | | | | diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index c40b475c3de..b432583ace5 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -75,4 +75,17 @@ OPERATOR_NAMESPACE=my-dev-odh-operator-system IMAGE_BUILD_FLAGS=--build-arg USE_LOCAL=true E2E_TEST_FLAGS="--skip-deletion=true" -timeout 15m DEFAULT_MANIFESTS_PATH=./opt/manifests -``` \ No newline at end of file +``` + +### When I try to use my own application namespace, I get different errors: + +1. Operator pod is keeping crash +Ensure in your cluster, only one application has label `opendatahub.io/application-namespace=true`. This is similar to case (3). + +2. error "DSCI must used the same namespace which has opendatahub.io/application-namespace=true label" +In the cluster, one namespace has label `opendatahub.io/application-namespace=true`, but it is not being set in the DSCI's `.spec.applicationsNamespace`, solutions (any of below ones should work): +- delete existin DSCI, and re-create it with namespace which already has label `opendatahub.io/application-namespace=true` +- remove label `opendatahub.io/application-namespace=true` from the other namespace to the one specified in the DSCI, and wait for a couple of minutes to allow DSCI continue. + +3. error "only support max. one namespace with label: opendatahub.io/application-namespace=true" +Refer to (1). diff --git a/get_all_manifests.sh b/get_all_manifests.sh index 9ec4da949e3..3995b03be6b 100755 --- a/get_all_manifests.sh +++ b/get_all_manifests.sh @@ -7,8 +7,8 @@ GITHUB_URL="https://github.com" # in the format of "repo-org:repo-name:ref-name:source-folder" and key is the target folder under manifests/ declare -A COMPONENT_MANIFESTS=( ["dashboard"]="opendatahub-io:odh-dashboard:main:manifests" - ["workbenches/kf-notebook-controller"]="opendatahub-io:kubeflow:v1.7-branch:components/notebook-controller/config" - ["workbenches/odh-notebook-controller"]="opendatahub-io:kubeflow:v1.7-branch:components/odh-notebook-controller/config" + ["workbenches/kf-notebook-controller"]="opendatahub-io:kubeflow:main:components/notebook-controller/config" + ["workbenches/odh-notebook-controller"]="opendatahub-io:kubeflow:main:components/odh-notebook-controller/config" ["workbenches/notebooks"]="opendatahub-io:notebooks:main:manifests" ["modelmeshserving"]="opendatahub-io:modelmesh-serving:release-0.12.0-rc0:config" ["kserve"]="opendatahub-io:kserve:release-v0.14:config" diff --git a/main.go b/main.go index d52b1ab8dd6..44b240f7d91 100644 --- a/main.go +++ b/main.go @@ -48,7 +48,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" @@ -98,8 +97,6 @@ import ( _ "github.com/opendatahub-io/opendatahub-operator/v2/controllers/components/workbenches" ) -const controllerNum = 4 // we should keep this updated if we have new controllers to add - var ( scheme = runtime.NewScheme() setupLog = ctrl.Log.WithName("setup") @@ -146,9 +143,7 @@ func main() { //nolint:funlen,maintidx,gocyclo var metricsAddr string var enableLeaderElection bool var probeAddr string - var dscApplicationsNamespace string var dscMonitoringNamespace string - var operatorName string var logmode string flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") @@ -156,11 +151,8 @@ func main() { //nolint:funlen,maintidx,gocyclo flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") - flag.StringVar(&dscApplicationsNamespace, "dsc-applications-namespace", "opendatahub", "The namespace where data science cluster"+ - "applications will be deployed") flag.StringVar(&dscMonitoringNamespace, "dsc-monitoring-namespace", "opendatahub", "The namespace where data science cluster"+ "monitoring stack will be deployed") - flag.StringVar(&operatorName, "operator-name", "opendatahub", "The name of the operator") flag.StringVar(&logmode, "log-mode", "", "Log mode ('', prod, devel), default to ''") opts := zap.Options{} @@ -179,9 +171,6 @@ func main() { //nolint:funlen,maintidx,gocyclo setupLog.Error(err, "error getting config for setup") os.Exit(1) } - // uplift default limiataions - setupCfg.QPS = rest.DefaultQPS * controllerNum // 5 * 4 controllers - setupCfg.Burst = rest.DefaultBurst * controllerNum // 10 * 4 controllers setupClient, err := client.New(setupCfg, client.Options{Scheme: scheme}) if err != nil { @@ -301,6 +290,8 @@ func main() { //nolint:funlen,maintidx,gocyclo Cache: &client.CacheOptions{ DisableFor: []client.Object{ resources.GvkToUnstructured(gvk.OpenshiftIngress), + &ofapiv1alpha1.Subscription{}, + resources.GvkToUnstructured(gvk.ServiceMeshControlPlane), &authorizationv1.SelfSubjectRulesReview{}, }, // Set it to true so the cache-backed client reads unstructured objects @@ -323,10 +314,9 @@ func main() { //nolint:funlen,maintidx,gocyclo } if err = (&dscictrl.DSCInitializationReconciler{ - Client: oc, - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("dscinitialization-controller"), - ApplicationsNamespace: dscApplicationsNamespace, + Client: oc, + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("dscinitialization-controller"), }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "DSCInitiatlization") os.Exit(1) @@ -403,7 +393,7 @@ func main() { //nolint:funlen,maintidx,gocyclo setupLog.Info("DSCI auto creation is disabled") } else { var createDefaultDSCIFunc manager.RunnableFunc = func(ctx context.Context) error { - err := upgrade.CreateDefaultDSCI(ctx, setupClient, platform, dscApplicationsNamespace, dscMonitoringNamespace) + err := upgrade.CreateDefaultDSCI(ctx, setupClient, platform, dscMonitoringNamespace) if err != nil { setupLog.Error(err, "unable to create initial setup for the operator") } @@ -433,7 +423,7 @@ func main() { //nolint:funlen,maintidx,gocyclo } // Cleanup resources from previous v2 releases var cleanExistingResourceFunc manager.RunnableFunc = func(ctx context.Context) error { - if err = upgrade.CleanupExistingResource(ctx, setupClient, platform, dscApplicationsNamespace, dscMonitoringNamespace, oldReleaseVersion); err != nil { + if err = upgrade.CleanupExistingResource(ctx, setupClient, platform, dscMonitoringNamespace, oldReleaseVersion); err != nil { setupLog.Error(err, "unable to perform cleanup") } return err @@ -476,6 +466,7 @@ func getCommonCache(ctx context.Context, cli client.Client, platform cluster.Pla if platform == cluster.ManagedRhoai { namespaceConfigs["redhat-ods-monitoring"] = cache.Config{} namespaceConfigs["redhat-ods-applications"] = cache.Config{} + namespaceConfigs[cluster.NamespaceConsoleLink] = cache.Config{} return namespaceConfigs, nil } cNamespaceList := &corev1.NamespaceList{} @@ -490,12 +481,14 @@ func getCommonCache(ctx context.Context, cli client.Client, platform cluster.Pla case 0: if platform == cluster.SelfManagedRhoai { namespaceConfigs["redhat-ods-applications"] = cache.Config{} + namespaceConfigs["redhat-ods-monitoring"] = cache.Config{} // since we still create monitoring namespace for self-managed return namespaceConfigs, nil } namespaceConfigs["opendatahub"] = cache.Config{} return namespaceConfigs, nil case 1: namespaceConfigs[cNamespaceList.Items[0].Name] = cache.Config{} + namespaceConfigs["redhat-ods-monitoring"] = cache.Config{} // since we still create monitoring namespace for self-managed default: return map[string]cache.Config{}, errors.New("only support max. one namespace with label: opendatahub.io/application-namespace: true") } diff --git a/pkg/cluster/const.go b/pkg/cluster/const.go index af8242ded2d..68c6434b6b1 100644 --- a/pkg/cluster/const.go +++ b/pkg/cluster/const.go @@ -18,4 +18,8 @@ const ( // Default OpenShift version CR name. OpenShiftVersionObj = "version" + + // Managed cluster required route. + NameConsoleLink = "console" + NamespaceConsoleLink = "openshift-console" ) diff --git a/pkg/cluster/gvk/gvk.go b/pkg/cluster/gvk/gvk.go index 240e4408e38..be654aaddd9 100644 --- a/pkg/cluster/gvk/gvk.go +++ b/pkg/cluster/gvk/gvk.go @@ -8,6 +8,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1" + featuresv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/features/v1" serviceApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/services/v1alpha1" ) @@ -28,6 +29,11 @@ var ( Version: "v1", Kind: "DSCInitialization", } + FeatureTracker = schema.GroupVersionKind{ + Group: featuresv1.GroupVersion.Group, + Version: featuresv1.GroupVersion.Version, + Kind: "FeatureTracker", + } Deployment = schema.GroupVersionKind{ Group: appsv1.SchemeGroupVersion.Group, diff --git a/pkg/cluster/meta.go b/pkg/cluster/meta.go index 659aa8112df..38108278a0c 100644 --- a/pkg/cluster/meta.go +++ b/pkg/cluster/meta.go @@ -37,6 +37,12 @@ func OwnedBy(owner metav1.Object, scheme *runtime.Scheme) MetaOptions { } } +func ControlledBy(owner metav1.Object, scheme *runtime.Scheme) MetaOptions { + return func(obj metav1.Object) error { + return controllerutil.SetControllerReference(owner, obj, scheme) + } +} + func WithLabels(labels ...string) MetaOptions { return func(obj metav1.Object) error { labelsMap, err := extractKeyValues(labels) diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index 9435e7b4df0..aac77c60883 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -4,10 +4,8 @@ import ( "context" "errors" "fmt" - "strconv" "time" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" k8serr "k8s.io/apimachinery/pkg/api/errors" @@ -15,8 +13,6 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels" ) // UpdatePodSecurityRolebinding update default rolebinding which is created in applications namespace by manifests @@ -56,36 +52,6 @@ func SubjectExistInRoleBinding(subjectList []rbacv1.Subject, serviceAccountName, return false } -// CreateSecret creates secrets required by dashboard component in downstream. -func CreateSecret(ctx context.Context, cli client.Client, name, namespace string, metaOptions ...MetaOptions) error { - desiredSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Type: corev1.SecretTypeOpaque, - } - - if err := ApplyMetaOptions(desiredSecret, metaOptions...); err != nil { - return err - } - - foundSecret := &corev1.Secret{} - err := cli.Get(ctx, client.ObjectKeyFromObject(desiredSecret), foundSecret) - if err != nil { - if k8serr.IsNotFound(err) { - err = cli.Create(ctx, desiredSecret) - if err != nil && !k8serr.IsAlreadyExists(err) { - return err - } - } else { - return err - } - } - - return nil -} - // CreateOrUpdateConfigMap creates a new configmap or updates an existing one. // If the configmap already exists, it will be updated with the merged Data and MetaOptions, if any. // ConfigMap.ObjectMeta.Name and ConfigMap.ObjectMeta.Namespace are both required, it returns an error otherwise. @@ -175,30 +141,6 @@ func ExecuteOnAllNamespaces(ctx context.Context, cli client.Client, processFunc return nil } -// WaitForDeploymentAvailable to check if component deployment from 'namespace' is ready within 'timeout' before apply prometheus rules for the component. -func WaitForDeploymentAvailable(ctx context.Context, c client.Client, componentName string, namespace string, interval int, timeout int) error { - log := logf.FromContext(ctx) - resourceInterval := time.Duration(interval) * time.Second - resourceTimeout := time.Duration(timeout) * time.Minute - - return wait.PollUntilContextTimeout(ctx, resourceInterval, resourceTimeout, true, func(ctx context.Context) (bool, error) { - componentDeploymentList := &appsv1.DeploymentList{} - err := c.List(ctx, componentDeploymentList, client.InNamespace(namespace), client.HasLabels{labels.ODH.Component(componentName)}) - if err != nil { - return false, fmt.Errorf("error fetching list of deployments: %w", err) - } - - log.Info("waiting for " + strconv.Itoa(len(componentDeploymentList.Items)) + " deployment to be ready for " + componentName) - for _, deployment := range componentDeploymentList.Items { - if deployment.Status.ReadyReplicas != deployment.Status.Replicas { - return false, nil - } - } - - return true, nil - }) -} - func CreateWithRetry(ctx context.Context, cli client.Client, obj client.Object, timeoutMin int) error { log := logf.FromContext(ctx) interval := time.Second * 5 // arbitrary value diff --git a/pkg/cluster/roles.go b/pkg/cluster/roles.go deleted file mode 100644 index 96ccbae0eb4..00000000000 --- a/pkg/cluster/roles.go +++ /dev/null @@ -1,89 +0,0 @@ -package cluster - -import ( - "context" - - rbacv1 "k8s.io/api/rbac/v1" - k8serr "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// CreateOrUpdateClusterRole creates cluster role based on define PolicyRules and optional metadata fields and updates the rules if it already exists. -func CreateOrUpdateClusterRole(ctx context.Context, cli client.Client, name string, rules []rbacv1.PolicyRule, metaOptions ...MetaOptions) (*rbacv1.ClusterRole, error) { - desiredClusterRole := &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Rules: rules, - } - - if err := ApplyMetaOptions(desiredClusterRole, metaOptions...); err != nil { - return nil, err - } - - foundClusterRole := &rbacv1.ClusterRole{} - err := cli.Get(ctx, client.ObjectKeyFromObject(desiredClusterRole), foundClusterRole) - if k8serr.IsNotFound(err) { - return desiredClusterRole, cli.Create(ctx, desiredClusterRole) - } - - if err := ApplyMetaOptions(foundClusterRole, metaOptions...); err != nil { - return nil, err - } - foundClusterRole.Rules = rules - - return foundClusterRole, cli.Update(ctx, foundClusterRole) -} - -// DeleteClusterRole simply calls delete on a ClusterRole with the given name. Any error is returned. Check for IsNotFound. -func DeleteClusterRole(ctx context.Context, cli client.Client, name string) error { - desiredClusterRole := &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - } - return cli.Delete(ctx, desiredClusterRole) -} - -// CreateOrUpdateClusterRoleBinding creates cluster role bindings based on define PolicyRules and optional metadata fields and updates the bindings if it already exists. -func CreateOrUpdateClusterRoleBinding(ctx context.Context, cli client.Client, name string, - subjects []rbacv1.Subject, roleRef rbacv1.RoleRef, - metaOptions ...MetaOptions) (*rbacv1.ClusterRoleBinding, error) { - desiredClusterRoleBinding := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Subjects: subjects, - RoleRef: roleRef, - } - - if err := ApplyMetaOptions(desiredClusterRoleBinding, metaOptions...); err != nil { - return nil, err - } - - foundClusterRoleBinding := &rbacv1.ClusterRoleBinding{} - err := cli.Get(ctx, client.ObjectKeyFromObject(desiredClusterRoleBinding), foundClusterRoleBinding) - if k8serr.IsNotFound(err) { - return desiredClusterRoleBinding, cli.Create(ctx, desiredClusterRoleBinding) - } - - if err := ApplyMetaOptions(foundClusterRoleBinding, metaOptions...); err != nil { - return nil, err - } - foundClusterRoleBinding.Subjects = subjects - foundClusterRoleBinding.RoleRef = roleRef - - return foundClusterRoleBinding, cli.Update(ctx, foundClusterRoleBinding) -} - -// DeleteClusterRoleBinding simply calls delete on a ClusterRoleBinding with the given name. Any error is returned. Check for IsNotFound. -func DeleteClusterRoleBinding(ctx context.Context, cli client.Client, name string) error { - desiredClusterRoleBinding := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - } - - return cli.Delete(ctx, desiredClusterRoleBinding) -} diff --git a/pkg/common/common.go b/pkg/common/common.go index 815308e4e33..e49cb8cf97a 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -70,34 +70,6 @@ func MatchLineInFile(fileName string, replacements map[string]string) error { return nil } -func TrimToRFC1123Name(input string) string { - if len(input) == 0 { - return input - } - if len(input) > 63 { - input = input[:63] - } - - regex := regexp.MustCompile(`[^A-Za-z0-9\-]+`) - replaced := regex.ReplaceAllString(input, "-") - - if !isAlphanumeric(replaced[0]) { - replaced = "a" + replaced[1:] - } - - if !isAlphanumeric(replaced[len(replaced)-1]) { - replaced = replaced[:len(replaced)-1] + "z" - } - - return strings.ToLower(replaced) -} - -func isAlphanumeric(char byte) bool { - regex := regexp.MustCompile(`^[A-Za-z0-9]$`) - - return regex.Match([]byte{char}) -} - // encode configmap data and return in base64. func GetMonitoringData(data string) (string, error) { // Create a new SHA-256 hash object diff --git a/pkg/common/common_suite_test.go b/pkg/common/common_suite_test.go deleted file mode 100644 index 353da190026..00000000000 --- a/pkg/common/common_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package common_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestK8sNamingHelpers(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Common k8s naming func unit tests") -} diff --git a/pkg/common/k8s_naming_test.go b/pkg/common/k8s_naming_test.go deleted file mode 100644 index 1b2c53f195d..00000000000 --- a/pkg/common/k8s_naming_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package common_test - -import ( - "github.com/opendatahub-io/opendatahub-operator/v2/pkg/common" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("Ensuring name (e.g. meta.Name) fulfills RFC1123 naming spec", func() { - - type nameConversionCase struct { - actual string - expected string - } - - DescribeTable("trimming to correct RFC1123 names", - func(testCase nameConversionCase) { - Expect(common.TrimToRFC1123Name(testCase.actual)).To(Equal(testCase.expected)) - }, - Entry("empty string should be left unchanged", nameConversionCase{ - actual: "", - expected: "", - }), - Entry("string longer than 63 characters should be trimmed to 63", nameConversionCase{ - actual: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmno", - expected: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk", - }), - Entry("string with non-alphanumeric characters should have them replaced to hyphens", nameConversionCase{ - actual: "abc!@#def", - expected: "abc-def", - }), - Entry("string starting with non-alphanumeric character should have it replaced", nameConversionCase{ - actual: "!abcdef", - expected: "aabcdef", - }), - Entry("string ending with non-alphanumeric character should have it replaced", nameConversionCase{ - actual: "abcdef!", - expected: "abcdefz", - }), - Entry("string with uppercase characters should be all lowercase", nameConversionCase{ - actual: "AbCdEf", - expected: "abcdef", - }), - Entry("string with multiple consecutive non-alphanumeric characters should have it folded to one hyphen", nameConversionCase{ - actual: "abc!!!def", - expected: "abc-def", - }), - Entry("string that has both start and end non-alphanumeric should have them replaced", nameConversionCase{ - actual: "!abcdef!", - expected: "aabcdefz", - }), - Entry("string that starts and ends with hyphens should have them replaced by alphanumeric characters", nameConversionCase{ - actual: "-abcdef-", - expected: "aabcdefz", - }), - Entry("string entirely of non-alphanumeric characters should be converted to one letter", nameConversionCase{ - actual: "!@#$%^&*()", - expected: "a", - }), - ) -}) diff --git a/pkg/controller/actions/deploy/action_deploy.go b/pkg/controller/actions/deploy/action_deploy.go index bb419f30f5c..a8614f0fa4c 100644 --- a/pkg/controller/actions/deploy/action_deploy.go +++ b/pkg/controller/actions/deploy/action_deploy.go @@ -134,7 +134,7 @@ func (a *Action) run(ctx context.Context, rr *odhTypes.ReconciliationRequest) er default: // Remove the DSC and DSCI owner reference if set, This is required during the // transition from the old to the new operator. - if err := removeOwnerReferences(ctx, rr.Client, current, isLegacyOwnerRef); err != nil { + if err := resources.RemoveOwnerReferences(ctx, rr.Client, current, isLegacyOwnerRef); err != nil { return err } diff --git a/pkg/controller/actions/deploy/action_deploy_support.go b/pkg/controller/actions/deploy/action_deploy_support.go index f6799a03af3..433b5f6473a 100644 --- a/pkg/controller/actions/deploy/action_deploy_support.go +++ b/pkg/controller/actions/deploy/action_deploy_support.go @@ -1,11 +1,7 @@ package deploy import ( - "context" - "fmt" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk" ) @@ -20,54 +16,3 @@ func isLegacyOwnerRef(or metav1.OwnerReference) bool { return false } } - -// removeOwnerReferences removes all owner references from a Kubernetes object that match the provided predicate. -// -// This function iterates through the OwnerReferences of the given object, filters out those that satisfy -// the predicate, and updates the object in the cluster using the provided client. -// -// Parameters: -// - ctx: The context for the request, which can carry deadlines, cancellation signals, and other request-scoped values. -// - cli: A controller-runtime client used to update the Kubernetes object. -// - obj: The Kubernetes object whose OwnerReferences are to be filtered. It must implement client.Object. -// - predicate: A function that takes an OwnerReference and returns true if the reference should be removed. -// -// Returns: -// - An error if the update operation fails, otherwise nil. -func removeOwnerReferences( - ctx context.Context, - cli client.Client, - obj client.Object, - predicate func(reference metav1.OwnerReference) bool, -) error { - oldRefs := obj.GetOwnerReferences() - if len(oldRefs) == 0 { - return nil - } - - newRefs := oldRefs[:0] - for _, ref := range oldRefs { - if !predicate(ref) { - newRefs = append(newRefs, ref) - } - } - - if len(newRefs) == len(oldRefs) { - return nil - } - - obj.SetOwnerReferences(newRefs) - - // Update the object in the cluster - if err := cli.Update(ctx, obj); err != nil { - return fmt.Errorf( - "failed to remove owner references from object %s/%s with gvk %s: %w", - obj.GetNamespace(), - obj.GetName(), - obj.GetObjectKind().GroupVersionKind(), - err, - ) - } - - return nil -} diff --git a/pkg/controller/actions/deploy/action_deploy_support_test.go b/pkg/controller/actions/deploy/action_deploy_support_test.go index 01396aa3a22..297c0e16113 100644 --- a/pkg/controller/actions/deploy/action_deploy_support_test.go +++ b/pkg/controller/actions/deploy/action_deploy_support_test.go @@ -2,30 +2,12 @@ package deploy import ( - "context" - "path/filepath" "testing" - "github.com/onsi/gomega/gstruct" "github.com/onsi/gomega/types" - "github.com/rs/xid" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/envtest" - componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1" - dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" - dsciv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/dscinitialization/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk" - odhCli "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/client" - "github.com/opendatahub-io/opendatahub-operator/v2/tests/envtestutil" . "github.com/onsi/gomega" ) @@ -97,93 +79,3 @@ func TestIsLegacyOwnerRef(t *testing.T) { }) } } - -func TestRemoveOwnerRef(t *testing.T) { - g := NewWithT(t) - s := runtime.NewScheme() - - ctx := context.Background() - ns := xid.New().String() - - utilruntime.Must(corev1.AddToScheme(s)) - utilruntime.Must(appsv1.AddToScheme(s)) - utilruntime.Must(apiextensionsv1.AddToScheme(s)) - utilruntime.Must(componentApi.AddToScheme(s)) - utilruntime.Must(dsciv1.AddToScheme(s)) - utilruntime.Must(dscv1.AddToScheme(s)) - utilruntime.Must(rbacv1.AddToScheme(s)) - - projectDir, err := envtestutil.FindProjectRoot() - g.Expect(err).NotTo(HaveOccurred()) - - envTest := &envtest.Environment{ - CRDInstallOptions: envtest.CRDInstallOptions{ - Scheme: s, - Paths: []string{ - filepath.Join(projectDir, "config", "crd", "bases"), - }, - ErrorIfPathMissing: true, - CleanUpAfterUse: false, - }, - } - - t.Cleanup(func() { - _ = envTest.Stop() - }) - - cfg, err := envTest.Start() - g.Expect(err).NotTo(HaveOccurred()) - - envTestClient, err := client.New(cfg, client.Options{Scheme: s}) - g.Expect(err).NotTo(HaveOccurred()) - - cli, err := odhCli.NewFromConfig(cfg, envTestClient) - g.Expect(err).NotTo(HaveOccurred()) - - err = cli.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}) - g.Expect(err).ToNot(HaveOccurred()) - - cm1 := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "cm1", Namespace: ns}} - cm1.SetGroupVersionKind(gvk.ConfigMap) - - err = cli.Create(ctx, cm1) - g.Expect(err).ToNot(HaveOccurred()) - - cm2 := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "cm2", Namespace: ns}} - cm2.SetGroupVersionKind(gvk.ConfigMap) - - err = cli.Create(ctx, cm2) - g.Expect(err).ToNot(HaveOccurred()) - - // Create a ConfigMap with OwnerReferences - configMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "test-configmap", Namespace: ns}} - - err = controllerutil.SetOwnerReference(cm1, configMap, s) - g.Expect(err).ToNot(HaveOccurred()) - err = controllerutil.SetOwnerReference(cm2, configMap, s) - g.Expect(err).ToNot(HaveOccurred()) - - err = cli.Create(ctx, configMap) - g.Expect(err).ToNot(HaveOccurred()) - - predicate := func(ref metav1.OwnerReference) bool { - return ref.Name == cm1.Name - } - - err = removeOwnerReferences(ctx, cli, configMap, predicate) - g.Expect(err).ToNot(HaveOccurred()) - - updatedConfigMap := &corev1.ConfigMap{} - err = cli.Get(ctx, client.ObjectKeyFromObject(configMap), updatedConfigMap) - g.Expect(err).ToNot(HaveOccurred()) - - g.Expect(updatedConfigMap.GetOwnerReferences()).Should(And( - HaveLen(1), - HaveEach(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal(cm2.Name), - "APIVersion": Equal(gvk.ConfigMap.GroupVersion().String()), - "Kind": Equal(gvk.ConfigMap.Kind), - "UID": Equal(cm2.UID), - })), - )) -} diff --git a/pkg/controller/actions/status/releases/action_fetch_releases_status.go b/pkg/controller/actions/status/releases/action_fetch_releases_status.go new file mode 100644 index 00000000000..f05b1a820c5 --- /dev/null +++ b/pkg/controller/actions/status/releases/action_fetch_releases_status.go @@ -0,0 +1,171 @@ +package releases + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/blang/semver/v4" + "github.com/operator-framework/api/pkg/lib/version" + "gopkg.in/yaml.v3" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/opendatahub-io/opendatahub-operator/v2/apis/common" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/types" + odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/resources" +) + +const ( + ComponentMetadataFilename = "component_metadata.yaml" +) + +// ComponentReleasesMeta represents the metadata for releases in the component_metadata.yaml file. +// It contains a list of releases associated with a specific component. +type ComponentReleasesMeta struct { + Releases []ComponentReleaseMeta `yaml:"releases,omitempty"` +} + +// ComponentReleaseMeta represents the metadata of a single release within the component_metadata.yaml file. +// It includes the release name, version, and repository URL. +type ComponentReleaseMeta struct { + // +required + Name string `yaml:"name"` + Version string `yaml:"version,omitempty"` + RepoURL string `yaml:"repoUrl,omitempty"` +} + +type Action struct { + metadataFilePath string + componentReleaseStatus []common.ComponentReleaseStatus +} + +// WithMetadataFilePath is an ActionOpts function that sets a custom metadata file path. +func WithMetadataFilePath(filePath string) ActionOpts { + return func(a *Action) { + a.metadataFilePath = filePath + } +} + +// WithComponentReleaseStatus allows setting a custom ComponentReleaseStatus for the action. +// This is mostly for testing purposes to simulate cached or pre-existing release statuses. +// In production scenarios, the action should fetch the current release status from the source rather than being manually set. +func WithComponentReleaseStatus(status []common.ComponentReleaseStatus) ActionOpts { + return func(a *Action) { + a.componentReleaseStatus = status + } +} + +type ActionOpts func(*Action) + +// run is responsible for executing the logic of reconciling and processing component releases. +// +// This function performs the following: +// 1. Verifies that the resource instance implements the `WithReleases` interface. +// 2. If the release status is not already cached, it calls the `render` method to fetch the releases from the metadata file. +// 3. Updates the release status on the resource instance with the processed release information. +// +// Parameters: +// - ctx: The context for managing deadlines and cancellations during the reconciliation process. +// - rr: The `ReconciliationRequest` containing the resource instance that needs to be reconciled. +// +// Returns: +// - An error if the reconciliation fails at any step. This could occur if the resource doesn't implement the required interface +// or if the metadata file cannot be read or processed. +func (a *Action) run(ctx context.Context, rr *types.ReconciliationRequest) error { + // Ensure the resource implements the WithReleases interface + obj, ok := rr.Instance.(common.WithReleases) + if !ok { + return fmt.Errorf("resource instance %v is not a WithReleases", rr.Instance) + } + + // If the release status is empty, or if the DevFlags.Manifests is set, render the release information. + // This ensures that releases are either reprocessed or fetched from the manifests specified in DevFlags. + if len(a.componentReleaseStatus) == 0 || resources.InstanceHasDevFlags(rr.Instance) { + releases, err := a.render(ctx, rr) + if err != nil { + return err + } + a.componentReleaseStatus = releases + } + + // Update the release status in the resource + obj.SetReleaseStatus(a.componentReleaseStatus) + + return nil +} + +// render reads and processes the component releases from the metadata file. +// +// This function performs the following: +// 1. Reads the component metadata YAML file (either from a custom or default path). +// 2. Parses the YAML file and extracts the release metadata (name, version, repo URL). +// 3. Returns a slice of `ComponentReleaseStatus` containing the processed release information. +// +// Parameters: +// - rr: The `ReconciliationRequest` containing the resource instance. This is used to determine the metadata file path. +// +// Returns: +// - A slice of `common.ComponentReleaseStatus`, representing the parsed release information from the metadata file. +// - An error if there is an issue with reading the file, unmarshalling the YAML, or processing the release data. +func (a *Action) render(ctx context.Context, rr *types.ReconciliationRequest) ([]common.ComponentReleaseStatus, error) { + log := logf.FromContext(ctx) + + // Determine the metadata file path + var metadataPath string + if a.metadataFilePath != "" { + metadataPath = a.metadataFilePath + } else { + // Build the path to the component metadata file + controllerName := strings.ToLower(rr.Instance.GetObjectKind().GroupVersionKind().Kind) + metadataPath = filepath.Join(odhdeploy.DefaultManifestPath, controllerName, ComponentMetadataFilename) + } + + // Read the YAML file + yamlData, err := os.ReadFile(metadataPath) + if err != nil { + if os.IsNotExist(err) { + // Log a message indicating the file doesn't exist but do not return an error + // Log this as a warning, as it's not necessarily a failure if the file is absent + log.V(3).Info("Metadata file not found, proceeding with empty releases", "metadataFilePath", metadataPath) + // Return an empty slice of releases instead of an error + return nil, nil + } + return nil, fmt.Errorf("error reading metadata file: %w", err) + } + + // Unmarshal YAML into defined struct + var componentMeta ComponentReleasesMeta + if err := yaml.Unmarshal(yamlData, &componentMeta); err != nil { + return nil, fmt.Errorf("error unmarshaling YAML: %w", err) + } + + // Parse and populate releases + componentReleasesStatus := make([]common.ComponentReleaseStatus, 0, len(componentMeta.Releases)) + for _, release := range componentMeta.Releases { + componentVersion, err := semver.Parse(strings.Trim(release.Version, "v")) + if err != nil { + return nil, fmt.Errorf("invalid version format for release %s: %w", release.Name, err) + } + componentReleasesStatus = append(componentReleasesStatus, common.ComponentReleaseStatus{ + Name: release.Name, + Version: version.OperatorVersion{Version: componentVersion}, + RepoURL: release.RepoURL, + }) + } + + return componentReleasesStatus, nil +} + +func NewAction(opts ...ActionOpts) actions.Fn { + action := Action{} + + for _, opt := range opts { + opt(&action) + } + + return action.run +} diff --git a/pkg/controller/actions/status/releases/action_fetch_releases_status_test.go b/pkg/controller/actions/status/releases/action_fetch_releases_status_test.go new file mode 100644 index 00000000000..f8e868bb0e4 --- /dev/null +++ b/pkg/controller/actions/status/releases/action_fetch_releases_status_test.go @@ -0,0 +1,230 @@ +package releases_test + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/blang/semver/v4" + "github.com/operator-framework/api/pkg/lib/version" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/opendatahub-io/opendatahub-operator/v2/apis/common" + componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/actions/status/releases" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/types" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestFetchReleasesStatusAction(t *testing.T) { + t.Helper() + + g := NewWithT(t) + ctx := context.Background() + + // Root directory for temporary test files + tempDir := filepath.Join(os.TempDir(), "releases_test") + + // Clean up created files after each test + AfterEach(func() { + // Remove the temporary test files + err := os.RemoveAll(tempDir) + Expect(err).NotTo(HaveOccurred()) + }) + + // Define a test cases + tests := []struct { + name string + metadataFilePath string + metadataContent string + expectedReleases int + expectedError bool + devFlagsEnabled bool + providedStatus []common.ComponentReleaseStatus // Provided ReleaseStatus for testing cache behavior + }{ + { + name: "should successfully render releases from valid YAML", + metadataFilePath: filepath.Join(tempDir, "valid_file.yaml"), + metadataContent: ` +releases: + - name: Kubeflow Pipelines + version: 2.2.0 + repoUrl: https://github.com/kubeflow/kfp-tekton + - name: Another Component + version: 1.3.1 + repoUrl: https://example.com/repo +`, + expectedReleases: 2, + expectedError: false, + }, + { + name: "should handle empty metadata file and return empty releases", + metadataFilePath: filepath.Join(tempDir, "empty_file.yaml"), + metadataContent: "", + expectedReleases: 0, + expectedError: false, + }, + { + name: "should fail if YAML is invalid and return empty releases", + metadataFilePath: filepath.Join(tempDir, "invalid_file.yaml"), + metadataContent: ` +releases: + - name: Kubeflow Pipelines + versionNumber: 2.2.0 + repoUrl: https://github.com/kubeflow/kfp-tekton +`, + expectedReleases: 0, + expectedError: true, + }, + { + name: "should fail if version format is invalid and return empty releases", + metadataFilePath: filepath.Join(tempDir, "invalid_version_field_file.yaml"), + metadataContent: ` +releases: + - name: Component1 + version: invalid-version + repoUrl: https://example.com/repo +`, + expectedReleases: 0, + expectedError: true, + }, + { + name: "should handle empty metadata file path gracefully", + metadataFilePath: "", + metadataContent: "", + expectedReleases: 0, + expectedError: false, + }, + { + name: "should not re-render releases if cached", + metadataFilePath: filepath.Join(tempDir, "cached_file.yaml"), + metadataContent: ` +releases: + - name: Kubeflow Pipelines + version: 2.2.0 + repoUrl: https://github.com/kubeflow/kfp-tekton +`, + expectedReleases: 1, + expectedError: false, + providedStatus: []common.ComponentReleaseStatus{ + { // Simulating cached status + Name: "Kubeflow Pipelines", + // dummy version set to name "", version 0.0.0 + Version: version.OperatorVersion{ + Version: semver.Version{}, + }, + RepoURL: "https://github.com/kubeflow/kfp-tekton", + }, + }, + }, + { + name: "should re-render releases if DevFlags are enabled", + metadataFilePath: filepath.Join(tempDir, "dev_flags_enabled_file.yaml"), + metadataContent: ` +releases: + - name: Kubeflow Pipelines + version: 2.2.0 + repoUrl: https://github.com/kubeflow/kfp-tekton +`, + expectedReleases: 1, + expectedError: false, + devFlagsEnabled: true, + providedStatus: []common.ComponentReleaseStatus{ + { // Simulating cached status + Name: "Kubeflow Pipelines", + // dummy version set to name "", version 0.0.0 + Version: version.OperatorVersion{ + Version: semver.Version{}, + }, + RepoURL: "https://github.com/kubeflow/kfp-tekton", + }, + }, + }, + } + + // Iterate through all test cases + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create the mock metadata file if needed + if tt.metadataContent != "" && tt.metadataFilePath != "" { + // Ensure the directory exists + err := os.MkdirAll(filepath.Dir(tt.metadataFilePath), 0755) + if err != nil { + t.Fatalf("failed to create directories: %v", err) + } + + // Write the test metadata content to the mock file + err = os.WriteFile(tt.metadataFilePath, []byte(tt.metadataContent), 0600) + if err != nil { + t.Fatalf("failed to write file: %v", err) + } + } + + // Mocking DevFlags if enabled (if DevFlagsEnabled is true, simulate dev flags) + devFlagsSpec := common.DevFlagsSpec{} + if tt.devFlagsEnabled { + devFlagsSpec.DevFlags = &common.DevFlags{ + Manifests: []common.ManifestsConfig{{URI: "github.com/kubeflow/kfp-tekton"}}, + } + } + + // Create the ReconciliationRequest and set a dummy resource instance + rr := types.ReconciliationRequest{ + Instance: &componentApi.DataSciencePipelines{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mock-instance", + }, + + // Mocking DevFlags if enabled (if DevFlagsEnabled is true, simulate dev flags) + Spec: componentApi.DataSciencePipelinesSpec{ + DataSciencePipelinesCommonSpec: componentApi.DataSciencePipelinesCommonSpec{ + DevFlagsSpec: devFlagsSpec, + }, + }, + }, + } + + // Check the number of componentReleases set on the instance + withReleasesInstance, ok := rr.Instance.(common.WithReleases) + if !ok { + t.Fatalf("Instance does not implement WithReleases") + } + + // Set up the action with the custom metadata file path and provided status + action := releases.NewAction( + releases.WithMetadataFilePath(tt.metadataFilePath), + releases.WithComponentReleaseStatus(tt.providedStatus), // Use WithComponentReleaseStatus to set the provided status + ) + + // Run the render action + err := action(ctx, &rr) + + // Validate results + if tt.expectedError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).NotTo(HaveOccurred()) + } + + // Get release status after action + finalReleases := withReleasesInstance.GetReleaseStatus() + + // Verify that the status is updated based on the caching and DevFlags + if tt.providedStatus != nil { + if tt.devFlagsEnabled { + // DevFlags are enabled, expect re-render (new version) + g.Expect(*finalReleases).NotTo(Equal(tt.providedStatus)) + } else { + // Cache is available, no DevFlags, expect no re-render (cached version) + g.Expect(*finalReleases).To(Equal(tt.providedStatus)) + } + } + + // Validate the expected release count after action + g.Expect(*finalReleases).To(HaveLen(tt.expectedReleases)) + }) + } +} diff --git a/pkg/controller/predicates/resources/resources.go b/pkg/controller/predicates/resources/resources.go index 1ff5c8734b0..bbb48296467 100644 --- a/pkg/controller/predicates/resources/resources.go +++ b/pkg/controller/predicates/resources/resources.go @@ -9,6 +9,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" + dsciv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/dscinitialization/v1" ) var _ predicate.Predicate = DeploymentPredicate{} @@ -117,3 +118,27 @@ var DSCComponentUpdatePredicate = predicate.Funcs{ return false }, } + +var DSCIReadiness = predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + oldObj, ok := e.ObjectOld.(*dsciv1.DSCInitialization) + if !ok { + return false + } + newObj, ok := e.ObjectNew.(*dsciv1.DSCInitialization) + if !ok { + return false + } + + return oldObj.Status.Phase != newObj.Status.Phase + }, + CreateFunc: func(e event.CreateEvent) bool { + return false + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return false + }, + GenericFunc: func(e event.GenericEvent) bool { + return false + }, +} diff --git a/pkg/deploy/deploy.go b/pkg/deploy/deploy.go index cb28d2c5239..498bce3b395 100644 --- a/pkg/deploy/deploy.go +++ b/pkg/deploy/deploy.go @@ -281,12 +281,6 @@ func manageResource(ctx context.Context, cli client.Client, res *resource.Resour if err == nil { // when resource is found if enabled { - // Exception to not update kserve with managed annotation - // do not reconcile kserve resource with annotation "opendatahub.io/managed: false" - // TODO: remove this exception when we define managed annotation across odh - if found.GetAnnotations()[annotations.ManagedByODHOperator] == "false" && componentName == "kserve" { - return nil - } return updateResource(ctx, cli, res, found, owner) } // Delete resource if it exists or do nothing if not found @@ -354,20 +348,15 @@ func createResource(ctx context.Context, cli client.Client, res *resource.Resour if err != nil { return err } - if obj.GetKind() != "CustomResourceDefinition" && obj.GetKind() != "OdhDashboardConfig" { - if err := ctrl.SetControllerReference(owner, metav1.Object(obj), cli.Scheme()); err != nil { - return err - } + + if err := ctrl.SetControllerReference(owner, metav1.Object(obj), cli.Scheme()); err != nil { + return err } + return cli.Create(ctx, obj) } -// Exception to skip ODHDashboardConfig CR reconcile. func updateResource(ctx context.Context, cli client.Client, res *resource.Resource, found *unstructured.Unstructured, owner metav1.Object) error { - if found.GetKind() == "OdhDashboardConfig" { - return nil - } - // Operator reconcile allowedListfield only when resource is managed by operator(annotation is true) // all other cases: no annotation at all, required annotation not present, of annotation is non-true value, skip reconcile if managed := found.GetAnnotations()[annotations.ManagedByODHOperator]; managed != "true" { diff --git a/pkg/feature/builder.go b/pkg/feature/builder.go index 54fadcabbec..83b7b14e862 100644 --- a/pkg/feature/builder.go +++ b/pkg/feature/builder.go @@ -19,6 +19,7 @@ type featureBuilder struct { managed bool source featurev1.Source owner metav1.Object + controller bool targetNs string builders []partialBuilder @@ -95,6 +96,12 @@ func (fb *featureBuilder) OwnedBy(object metav1.Object) *featureBuilder { return fb } +func (fb *featureBuilder) Controller(controller bool) *featureBuilder { + fb.controller = controller + + return fb +} + // Managed marks the feature as managed by the operator. This effectively marks all resources which are part of this feature // as those that should be updated on operator reconcile. // Managed marks the feature as managed by the operator. @@ -193,12 +200,13 @@ func (fb *featureBuilder) Create() (*Feature, error) { } f := &Feature{ - Name: fb.featureName, - Managed: fb.managed, - Enabled: alwaysEnabled, - Log: log.Log.WithName("features").WithValues("feature", fb.featureName), - source: &fb.source, - owner: fb.owner, + Name: fb.featureName, + Managed: fb.managed, + Enabled: alwaysEnabled, + Log: log.Log.WithName("features").WithValues("feature", fb.featureName), + source: &fb.source, + owner: fb.owner, + controller: fb.controller, } for i := range fb.builders { diff --git a/pkg/feature/feature.go b/pkg/feature/feature.go index 33e569a0fd5..4d16c094751 100644 --- a/pkg/feature/feature.go +++ b/pkg/feature/feature.go @@ -7,6 +7,7 @@ import ( "github.com/go-logr/logr" "github.com/hashicorp/go-multierror" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" featurev1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/features/v1" @@ -43,9 +44,10 @@ type Feature struct { Log logr.Logger - tracker *featurev1.FeatureTracker - source *featurev1.Source - owner metav1.Object + tracker *featurev1.FeatureTracker + source *featurev1.Source + owner metav1.Object + controller bool data map[string]any @@ -168,8 +170,22 @@ func OwnedBy(f *Feature) cluster.MetaOptions { return cluster.WithOwnerReference(f.AsOwnerReference()) } +func ControlledBy(f *Feature) cluster.MetaOptions { + or := f.AsOwnerReference() + or.Controller = ptr.To[bool](true) + or.BlockOwnerDeletion = ptr.To[bool](true) + return cluster.WithOwnerReference(or) +} + func DefaultMetaOptions(f *Feature) []cluster.MetaOptions { - resourceMeta := []cluster.MetaOptions{OwnedBy(f)} + resourceMeta := make([]cluster.MetaOptions, 0, 1) + + if f.controller { + resourceMeta = append(resourceMeta, ControlledBy(f)) + } else { + resourceMeta = append(resourceMeta, OwnedBy(f)) + } + if f.Managed { resourceMeta = append(resourceMeta, func(obj metav1.Object) error { objAnnotations := obj.GetAnnotations() diff --git a/pkg/feature/feature_tracker_handler.go b/pkg/feature/feature_tracker_handler.go index 6ba728a11d6..e1f1ca39c30 100644 --- a/pkg/feature/feature_tracker_handler.go +++ b/pkg/feature/feature_tracker_handler.go @@ -38,20 +38,34 @@ func createFeatureTracker(ctx context.Context, cli client.Client, f *Feature) er if k8serr.IsNotFound(errGet) { tracker = featurev1.NewFeatureTracker(f.Name, f.TargetNamespace) - tracker.Spec = featurev1.FeatureTrackerSpec{ - Source: *f.source, - AppNamespace: f.TargetNamespace, + } + + tracker.Spec = featurev1.FeatureTrackerSpec{ + Source: *f.source, + AppNamespace: f.TargetNamespace, + } + + if f.owner != nil { + var ownerRef cluster.MetaOptions + if f.controller { + ownerRef = cluster.ControlledBy(f.owner, cli.Scheme()) + } else { + ownerRef = cluster.OwnedBy(f.owner, cli.Scheme()) } - if f.owner != nil { - ownerRef := cluster.OwnedBy(f.owner, cli.Scheme()) - if errMetaOpts := cluster.ApplyMetaOptions(tracker, ownerRef); errMetaOpts != nil { - return fmt.Errorf("failed adding owner to FeatureTracker %s: %w", tracker.Name, errMetaOpts) - } + + if errMetaOpts := cluster.ApplyMetaOptions(tracker, ownerRef); errMetaOpts != nil { + return fmt.Errorf("failed adding owner to FeatureTracker %s: %w", tracker.Name, errMetaOpts) } + } + if k8serr.IsNotFound(errGet) { if errCreate := cli.Create(ctx, tracker); errCreate != nil { return fmt.Errorf("failed creating FeatureTracker %s: %w", tracker.Name, errCreate) } + } else { + if errUpdate := cli.Update(ctx, tracker); errUpdate != nil { + return fmt.Errorf("failed updating FeatureTracker %s: %w", tracker.Name, errUpdate) + } } if errGVK := ensureGVKSet(tracker, cli.Scheme()); errGVK != nil { diff --git a/pkg/feature/handler.go b/pkg/feature/handler.go index 890a51a1fcc..6e71d5e91df 100644 --- a/pkg/feature/handler.go +++ b/pkg/feature/handler.go @@ -30,6 +30,7 @@ var _ featuresHandler = (*FeaturesHandler)(nil) type FeaturesHandler struct { source featurev1.Source owner metav1.Object + controller bool targetNamespace string features []*Feature featuresProviders []FeaturesProvider @@ -47,6 +48,7 @@ func (fh *FeaturesHandler) Add(builders ...*featureBuilder) error { feature, err := fb. TargetNamespace(fh.targetNamespace). OwnedBy(fh.owner). + Controller(fh.controller). Source(fh.source). Create() multiErr = multierror.Append(multiErr, err) @@ -112,6 +114,7 @@ func ClusterFeaturesHandler(dsci *dsciv1.DSCInitialization, def ...FeaturesProvi func ComponentFeaturesHandler(owner metav1.Object, componentName, targetNamespace string, def ...FeaturesProvider) *FeaturesHandler { return &FeaturesHandler{ owner: owner, + controller: true, targetNamespace: targetNamespace, source: featurev1.Source{Type: featurev1.ComponentType, Name: componentName}, featuresProviders: def, diff --git a/pkg/feature/servicemesh/conditions.go b/pkg/feature/servicemesh/conditions.go index 6926a5dfe3f..2920f09f8a6 100644 --- a/pkg/feature/servicemesh/conditions.go +++ b/pkg/feature/servicemesh/conditions.go @@ -90,12 +90,15 @@ func WaitForControlPlaneToBeReady(ctx context.Context, cli client.Client, f *fea return wait.PollUntilContextTimeout(ctx, interval, duration, false, func(ctx context.Context) (bool, error) { ready, err := CheckControlPlaneComponentReadiness(ctx, cli, smcp, smcpNs) + if err != nil { + return false, err + } if ready { f.Log.Info("done waiting for control plane components to be ready", "control-plane", smcp, "namespace", smcpNs) } - return ready, err + return ready, nil }) } @@ -107,14 +110,20 @@ func CheckControlPlaneComponentReadiness(ctx context.Context, c client.Client, s Name: smcpName, }, smcpObj) - if err != nil { + switch { + case k8serr.IsNotFound(err): + return false, nil + case err != nil: return false, fmt.Errorf("failed to find Service Mesh Control Plane: %w", err) } components, found, err := unstructured.NestedMap(smcpObj.Object, "status", "readiness", "components") - if err != nil || !found { + if err != nil { return false, fmt.Errorf("status conditions not found or error in parsing of Service Mesh Control Plane: %w", err) } + if !found { + return false, nil + } readyComponents := len(components["ready"].([]interface{})) //nolint:forcetypeassert,errcheck pendingComponents := len(components["pending"].([]interface{})) //nolint:forcetypeassert,errcheck diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go index f5de7cf39a3..05b3d1354a3 100644 --- a/pkg/resources/resources.go +++ b/pkg/resources/resources.go @@ -2,6 +2,7 @@ package resources import ( "bytes" + "context" "crypto/sha256" "encoding/base64" "errors" @@ -13,6 +14,7 @@ import ( routev1 "github.com/openshift/api/route/v1" "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -321,9 +323,70 @@ func HasDevFlags(in common.WithDevFlags) bool { return df != nil && len(df.Manifests) != 0 } +// InstanceHasDevFlags checks if the given PlatformObject implements the WithDevFlags interface +// and if it has any DevFlags set. If the object does not implement WithDevFlags, it returns false. +// This function helps ensure that only objects with the WithDevFlags interface are processed for DevFlags. +func InstanceHasDevFlags(in common.PlatformObject) bool { + if obj, ok := in.(common.WithDevFlags); ok { + return HasDevFlags(obj) + } + return false +} + func NamespacedNameFromObject(obj client.Object) types.NamespacedName { return types.NamespacedName{ Namespace: obj.GetNamespace(), Name: obj.GetName(), } } + +// RemoveOwnerReferences removes all owner references from a Kubernetes object that match the provided predicate. +// +// This function iterates through the OwnerReferences of the given object, filters out those that satisfy +// the predicate, and updates the object in the cluster using the provided client. +// +// Parameters: +// - ctx: The context for the request, which can carry deadlines, cancellation signals, and other request-scoped values. +// - cli: A controller-runtime client used to update the Kubernetes object. +// - obj: The Kubernetes object whose OwnerReferences are to be filtered. It must implement client.Object. +// - predicate: A function that takes an OwnerReference and returns true if the reference should be removed. +// +// Returns: +// - An error if the update operation fails, otherwise nil. +func RemoveOwnerReferences( + ctx context.Context, + cli client.Client, + obj client.Object, + predicate func(reference metav1.OwnerReference) bool, +) error { + oldRefs := obj.GetOwnerReferences() + if len(oldRefs) == 0 { + return nil + } + + newRefs := oldRefs[:0] + for _, ref := range oldRefs { + if !predicate(ref) { + newRefs = append(newRefs, ref) + } + } + + if len(newRefs) == len(oldRefs) { + return nil + } + + obj.SetOwnerReferences(newRefs) + + // Update the object in the cluster + if err := cli.Update(ctx, obj); err != nil { + return fmt.Errorf( + "failed to remove owner references from object %s/%s with gvk %s: %w", + obj.GetNamespace(), + obj.GetName(), + obj.GetObjectKind().GroupVersionKind(), + err, + ) + } + + return nil +} diff --git a/pkg/resources/resources_test.go b/pkg/resources/resources_test.go index 88d4d589539..11289e4b199 100644 --- a/pkg/resources/resources_test.go +++ b/pkg/resources/resources_test.go @@ -1,16 +1,32 @@ package resources_test import ( + "context" "errors" + "path/filepath" "testing" + "github.com/onsi/gomega/gstruct" + "github.com/rs/xid" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/envtest" + + componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1" + dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" + dsciv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/dscinitialization/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk" + odhCli "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/client" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/resources" + "github.com/opendatahub-io/opendatahub-operator/v2/tests/envtestutil" . "github.com/onsi/gomega" ) @@ -136,3 +152,93 @@ func TestEnsureGroupVersionKind(t *testing.T) { g.Expect(err.Error()).To(ContainSubstring("failed to get GVK")) }) } + +func TestRemoveOwnerRef(t *testing.T) { + g := NewWithT(t) + s := runtime.NewScheme() + + ctx := context.Background() + ns := xid.New().String() + + utilruntime.Must(corev1.AddToScheme(s)) + utilruntime.Must(appsv1.AddToScheme(s)) + utilruntime.Must(apiextensionsv1.AddToScheme(s)) + utilruntime.Must(componentApi.AddToScheme(s)) + utilruntime.Must(dsciv1.AddToScheme(s)) + utilruntime.Must(dscv1.AddToScheme(s)) + utilruntime.Must(rbacv1.AddToScheme(s)) + + projectDir, err := envtestutil.FindProjectRoot() + g.Expect(err).NotTo(HaveOccurred()) + + envTest := &envtest.Environment{ + CRDInstallOptions: envtest.CRDInstallOptions{ + Scheme: s, + Paths: []string{ + filepath.Join(projectDir, "config", "crd", "bases"), + }, + ErrorIfPathMissing: true, + CleanUpAfterUse: false, + }, + } + + t.Cleanup(func() { + _ = envTest.Stop() + }) + + cfg, err := envTest.Start() + g.Expect(err).NotTo(HaveOccurred()) + + envTestClient, err := client.New(cfg, client.Options{Scheme: s}) + g.Expect(err).NotTo(HaveOccurred()) + + cli, err := odhCli.NewFromConfig(cfg, envTestClient) + g.Expect(err).NotTo(HaveOccurred()) + + err = cli.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}) + g.Expect(err).ToNot(HaveOccurred()) + + cm1 := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "cm1", Namespace: ns}} + cm1.SetGroupVersionKind(gvk.ConfigMap) + + err = cli.Create(ctx, cm1) + g.Expect(err).ToNot(HaveOccurred()) + + cm2 := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "cm2", Namespace: ns}} + cm2.SetGroupVersionKind(gvk.ConfigMap) + + err = cli.Create(ctx, cm2) + g.Expect(err).ToNot(HaveOccurred()) + + // Create a ConfigMap with OwnerReferences + configMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "test-configmap", Namespace: ns}} + + err = controllerutil.SetOwnerReference(cm1, configMap, s) + g.Expect(err).ToNot(HaveOccurred()) + err = controllerutil.SetOwnerReference(cm2, configMap, s) + g.Expect(err).ToNot(HaveOccurred()) + + err = cli.Create(ctx, configMap) + g.Expect(err).ToNot(HaveOccurred()) + + predicate := func(ref metav1.OwnerReference) bool { + return ref.Name == cm1.Name + } + + err = resources.RemoveOwnerReferences(ctx, cli, configMap, predicate) + g.Expect(err).ToNot(HaveOccurred()) + + updatedConfigMap := &corev1.ConfigMap{} + err = cli.Get(ctx, client.ObjectKeyFromObject(configMap), updatedConfigMap) + g.Expect(err).ToNot(HaveOccurred()) + + g.Expect(updatedConfigMap.GetOwnerReferences()).Should(And( + HaveLen(1), + HaveEach(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal(cm2.Name), + "APIVersion": Equal(gvk.ConfigMap.GroupVersion().String()), + "Kind": Equal(gvk.ConfigMap.Kind), + "UID": Equal(cm2.UID), + })), + )) +} diff --git a/pkg/upgrade/upgrade.go b/pkg/upgrade/upgrade.go index 9850e9e88e0..0ca00cbb6fb 100644 --- a/pkg/upgrade/upgrade.go +++ b/pkg/upgrade/upgrade.go @@ -10,9 +10,7 @@ import ( "github.com/hashicorp/go-multierror" operatorv1 "github.com/openshift/api/operator/v1" - routev1 "github.com/openshift/api/route/v1" templatev1 "github.com/openshift/api/template/v1" - monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -108,10 +106,9 @@ func CreateDefaultDSC(ctx context.Context, cli client.Client) error { // CreateDefaultDSCI creates a default instance of DSCI // If there exists default-dsci instance already, it will not update DSCISpec on it. // Note: DSCI CR modifcations are not supported, as it is the initial prereq setting for the components. -func CreateDefaultDSCI(ctx context.Context, cli client.Client, _ cluster.Platform, appNamespace, monNamespace string) error { +func CreateDefaultDSCI(ctx context.Context, cli client.Client, _ cluster.Platform, monNamespace string) error { log := logf.FromContext(ctx) defaultDsciSpec := &dsciv1.DSCInitializationSpec{ - ApplicationsNamespace: appNamespace, Monitoring: serviceApi.DSCMonitoring{ ManagementSpec: common.ManagementSpec{ManagementState: operatorv1.Managed}, MonitoringCommonSpec: serviceApi.MonitoringCommonSpec{ @@ -208,60 +205,33 @@ func getDashboardWatsonResources(ns string) []ResourceSpec { func CleanupExistingResource(ctx context.Context, cli client.Client, platform cluster.Platform, - dscApplicationsNamespace, dscMonitoringNamespace string, + dscMonitoringNamespace string, oldReleaseVersion cluster.Release, ) error { var multiErr *multierror.Error - // Special Handling of cleanup of deprecated model monitoring stack - if platform == cluster.ManagedRhoai { - deprecatedDeployments := []string{"rhods-prometheus-operator"} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscMonitoringNamespace, deprecatedDeployments, &appsv1.DeploymentList{})) - - deprecatedStatefulsets := []string{"prometheus-rhods-model-monitoring"} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscMonitoringNamespace, deprecatedStatefulsets, &appsv1.StatefulSetList{})) - - deprecatedServices := []string{"rhods-model-monitoring"} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscMonitoringNamespace, deprecatedServices, &corev1.ServiceList{})) - - deprecatedRoutes := []string{"rhods-model-monitoring"} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscMonitoringNamespace, deprecatedRoutes, &routev1.RouteList{})) - - deprecatedSecrets := []string{"rhods-monitoring-oauth-config"} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscMonitoringNamespace, deprecatedSecrets, &corev1.SecretList{})) - - deprecatedClusterroles := []string{"rhods-namespace-read", "rhods-prometheus-operator"} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscMonitoringNamespace, deprecatedClusterroles, &rbacv1.ClusterRoleList{})) - - deprecatedClusterrolebindings := []string{"rhods-namespace-read", "rhods-prometheus-operator"} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscMonitoringNamespace, deprecatedClusterrolebindings, &rbacv1.ClusterRoleBindingList{})) - - deprecatedServiceAccounts := []string{"rhods-prometheus-operator"} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscMonitoringNamespace, deprecatedServiceAccounts, &corev1.ServiceAccountList{})) - - deprecatedServicemonitors := []string{"modelmesh-federated-metrics"} - multiErr = multierror.Append(multiErr, deleteDeprecatedServiceMonitors(ctx, cli, dscMonitoringNamespace, deprecatedServicemonitors)) + // get DSCI CR to get application namespace + dsciList := &dsciv1.DSCInitializationList{} + if err := cli.List(ctx, dsciList); err != nil { + return err } - // common logic for both self-managed and managed - deprecatedOperatorSM := []string{"rhods-monitor-federation2"} - multiErr = multierror.Append(multiErr, deleteDeprecatedServiceMonitors(ctx, cli, dscMonitoringNamespace, deprecatedOperatorSM)) - - // Remove deprecated opendatahub namespace(previously owned by kuberay and Kueue) - multiErr = multierror.Append(multiErr, deleteDeprecatedNamespace(ctx, cli, "opendatahub")) - + if len(dsciList.Items) == 0 { + return nil + } + d := &dsciList.Items[0] // Handling for dashboard OdhApplication Jupyterhub CR, see jira #443 - multiErr = multierror.Append(multiErr, removOdhApplicationsCR(ctx, cli, gvk.OdhApplication, "jupyterhub", dscApplicationsNamespace)) + multiErr = multierror.Append(multiErr, removOdhApplicationsCR(ctx, cli, gvk.OdhApplication, "jupyterhub", d.Spec.ApplicationsNamespace)) // cleanup for github.com/opendatahub-io/pull/888 - deprecatedFeatureTrackers := []string{dscApplicationsNamespace + "-kserve-temporary-fixes"} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscApplicationsNamespace, deprecatedFeatureTrackers, &featuresv1.FeatureTrackerList{})) + deprecatedFeatureTrackers := []string{d.Spec.ApplicationsNamespace + "-kserve-temporary-fixes"} + multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, d.Spec.ApplicationsNamespace, deprecatedFeatureTrackers, &featuresv1.FeatureTrackerList{})) // Cleanup of deprecated default RoleBinding resources - deprecatedDefaultRoleBinding := []string{dscApplicationsNamespace} - multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, dscApplicationsNamespace, deprecatedDefaultRoleBinding, &rbacv1.RoleBindingList{})) + deprecatedDefaultRoleBinding := []string{d.Spec.ApplicationsNamespace} + multiErr = multierror.Append(multiErr, deleteDeprecatedResources(ctx, cli, d.Spec.ApplicationsNamespace, deprecatedDefaultRoleBinding, &rbacv1.RoleBindingList{})) // Handling for dashboard OdhDocument Jupyterhub CR, see jira #443 comments odhDocJPH := getJPHOdhDocumentResources( - dscApplicationsNamespace, + d.Spec.ApplicationsNamespace, []string{ "jupyterhub-install-python-packages", "jupyterhub-update-server-settings", @@ -271,23 +241,23 @@ func CleanupExistingResource(ctx context.Context, multiErr = multierror.Append(multiErr, deleteResources(ctx, cli, &odhDocJPH)) // only apply on RHOAI since ODH has a different way to create this CR by dashboard if platform == cluster.SelfManagedRhoai || platform == cluster.ManagedRhoai { - if err := upgradeODCCR(ctx, cli, "odh-dashboard-config", dscApplicationsNamespace, oldReleaseVersion); err != nil { + if err := upgradeODCCR(ctx, cli, "odh-dashboard-config", d.Spec.ApplicationsNamespace, oldReleaseVersion); err != nil { return err } } // remove modelreg proxy container from deployment in ODH if platform == cluster.OpenDataHub { - if err := removeRBACProxyModelRegistry(ctx, cli, "model-registry-operator", "kube-rbac-proxy", dscApplicationsNamespace); err != nil { + if err := removeRBACProxyModelRegistry(ctx, cli, "model-registry-operator", "kube-rbac-proxy", d.Spec.ApplicationsNamespace); err != nil { return err } } // to take a reference - toDelete := getDashboardWatsonResources(dscApplicationsNamespace) + toDelete := getDashboardWatsonResources(d.Spec.ApplicationsNamespace) multiErr = multierror.Append(multiErr, deleteResources(ctx, cli, &toDelete)) // cleanup nvidia nim integration - multiErr = multierror.Append(multiErr, cleanupNimIntegration(ctx, cli, oldReleaseVersion, dscApplicationsNamespace)) + multiErr = multierror.Append(multiErr, cleanupNimIntegration(ctx, cli, oldReleaseVersion, d.Spec.ApplicationsNamespace)) return multiErr.ErrorOrNil() } @@ -369,35 +339,6 @@ func deleteDeprecatedResources(ctx context.Context, cli client.Client, namespace return multiErr.ErrorOrNil() } -// Need to handle ServiceMonitor deletion separately as the generic function does not work for ServiceMonitors because of how the package is built. -func deleteDeprecatedServiceMonitors(ctx context.Context, cli client.Client, namespace string, resourceList []string) error { - log := logf.FromContext(ctx) - var multiErr *multierror.Error - listOpts := &client.ListOptions{Namespace: namespace} - servicemonitors := &monitoringv1.ServiceMonitorList{} - if err := cli.List(ctx, servicemonitors, listOpts); err != nil { - multiErr = multierror.Append(multiErr, err) - } - - for _, servicemonitor := range servicemonitors.Items { - for _, name := range resourceList { - if name == servicemonitor.Name { - log.Info("Attempting to delete " + servicemonitor.Name + " in namespace " + namespace) - err := cli.Delete(ctx, servicemonitor) - if err != nil { - if k8serr.IsNotFound(err) { - log.Info("Could not find " + servicemonitor.Name + " in namespace " + namespace) - } else { - multiErr = multierror.Append(multiErr, err) - } - } - log.Info("Successfully deleted " + servicemonitor.Name) - } - } - } - return multiErr.ErrorOrNil() -} - func removOdhApplicationsCR(ctx context.Context, cli client.Client, gvk schema.GroupVersionKind, instanceName string, applicationNS string) error { // first check if CRD in cluster crd := &apiextv1.CustomResourceDefinition{} @@ -526,63 +467,6 @@ func removeRBACProxyModelRegistry(ctx context.Context, cli client.Client, compon return nil } -func RemoveLabel(ctx context.Context, cli client.Client, objectName string, labelKey string) error { - foundNamespace := &corev1.Namespace{} - if err := cli.Get(ctx, client.ObjectKey{Name: objectName}, foundNamespace); err != nil { - if k8serr.IsNotFound(err) { - return nil - } - return fmt.Errorf("could not get %s namespace: %w", objectName, err) - } - delete(foundNamespace.Labels, labelKey) - if err := cli.Update(ctx, foundNamespace); err != nil { - return fmt.Errorf("error removing %s from %s : %w", labelKey, objectName, err) - } - return nil -} - -func deleteDeprecatedNamespace(ctx context.Context, cli client.Client, namespace string) error { - log := logf.FromContext(ctx) - foundNamespace := &corev1.Namespace{} - if err := cli.Get(ctx, client.ObjectKey{Name: namespace}, foundNamespace); err != nil { - if k8serr.IsNotFound(err) { - return nil - } - return fmt.Errorf("could not get %s namespace: %w", namespace, err) - } - - // Check if namespace is owned by DSC - isOwnedByDSC := false - for _, owner := range foundNamespace.OwnerReferences { - if owner.Kind == "DataScienceCluster" { - isOwnedByDSC = true - } - } - if !isOwnedByDSC { - return nil - } - - // Check if namespace has pods running - podList := &corev1.PodList{} - listOpts := []client.ListOption{ - client.InNamespace(namespace), - } - if err := cli.List(ctx, podList, listOpts...); err != nil { - return fmt.Errorf("error getting pods from namespace %s: %w", namespace, err) - } - if len(podList.Items) != 0 { - log.Info("Skip deletion of namespace " + namespace + " due to running Pods in it") - return nil - } - - // Delete namespace if no pods found - if err := cli.Delete(ctx, foundNamespace); err != nil { - return fmt.Errorf("could not delete %s namespace: %w", namespace, err) - } - - return nil -} - func GetDeployedRelease(ctx context.Context, cli client.Client) (cluster.Release, error) { dsciInstance := &dsciv1.DSCInitializationList{} if err := cli.List(ctx, dsciInstance); err != nil { diff --git a/tests/e2e/codeflare_test.go b/tests/e2e/codeflare_test.go index 26781260463..ce3f8e47eeb 100644 --- a/tests/e2e/codeflare_test.go +++ b/tests/e2e/codeflare_test.go @@ -22,6 +22,7 @@ func codeflareTestSuite(t *testing.T) { t.Run("Validate operands have OwnerReferences", componentCtx.ValidateOperandsOwnerReferences) t.Run("Validate update operand resources", componentCtx.ValidateUpdateDeploymentsResources) t.Run("Validate component disabled", componentCtx.ValidateComponentDisabled) + t.Run("Validate component releases", componentCtx.ValidateComponentReleases) } type CodeFlareTestCtx struct { diff --git a/tests/e2e/components_test.go b/tests/e2e/components_test.go index c7f37290d1a..4077b55c810 100644 --- a/tests/e2e/components_test.go +++ b/tests/e2e/components_test.go @@ -260,6 +260,46 @@ func (c *ComponentTestCtx) ValidateCRDReinstated(t *testing.T, name string) { ) } +// Validate releases for any component in the DataScienceCluster. +func (c *ComponentTestCtx) ValidateComponentReleases(t *testing.T) { + t.Helper() + + g := c.NewWithT(t) + + componentName := strings.ToLower(c.GVK.Kind) + + // Transform the DataScienceCluster to set the management state of the component + g.Update( + gvk.DataScienceCluster, + c.DSCName, + testf.Transform( + `.spec.components.%s.managementState = "%s"`, componentName, operatorv1.Managed, + ), + ).Eventually().Should( + jq.Match(`.spec.components.%s.managementState == "%s"`, componentName, operatorv1.Managed), + ) + + // Check if the releases field contains multiple releases for the component + g.List(gvk.DataScienceCluster).Eventually().Should(And( + HaveLen(1), + HaveEach( + // Check releases for the component itself + jq.Match(`.status.components.%s.releases | length > 0`, componentName), + ), + )) + + // Validate each release's fields (name, version, repoUrl) using HaveEach + g.List(gvk.DataScienceCluster).Eventually().Should(And( + HaveLen(1), + HaveEach(And( + // Check that each release has the required fields (name, version, repoUrl) + jq.Match(`.status.components.%s.releases[].name != ""`, componentName), + jq.Match(`.status.components.%s.releases[].version != ""`, componentName), + jq.Match(`.status.components.%s.releases[].repoUrl != ""`, componentName)), + ), + )) +} + func (c *ComponentTestCtx) GetDSC() (*dscv1.DataScienceCluster, error) { obj := dscv1.DataScienceCluster{} diff --git a/tests/e2e/datasciencepipelines_test.go b/tests/e2e/datasciencepipelines_test.go index b00f41e8133..5da02b82a73 100644 --- a/tests/e2e/datasciencepipelines_test.go +++ b/tests/e2e/datasciencepipelines_test.go @@ -22,6 +22,7 @@ func dataSciencePipelinesTestSuite(t *testing.T) { t.Run("Validate operands have OwnerReferences", componentCtx.ValidateOperandsOwnerReferences) t.Run("Validate update operand resources", componentCtx.ValidateUpdateDeploymentsResources) t.Run("Validate component disabled", componentCtx.ValidateComponentDisabled) + t.Run("Validate component releases", componentCtx.ValidateComponentReleases) } type DataSciencePipelinesTestCtx struct { diff --git a/tests/e2e/helper_test.go b/tests/e2e/helper_test.go index edaffd8fdf9..e838e0bf6a8 100644 --- a/tests/e2e/helper_test.go +++ b/tests/e2e/helper_test.go @@ -107,7 +107,7 @@ func setupDSCICR(name string) *dsciv1.DSCInitialization { ApplicationsNamespace: "opendatahub", Monitoring: serviceApi.DSCMonitoring{ ManagementSpec: common.ManagementSpec{ - ManagementState: operatorv1.Managed, + ManagementState: operatorv1.Removed, // keep rhoai branch to Managed so we can test it }, MonitoringCommonSpec: serviceApi.MonitoringCommonSpec{ Namespace: "opendatahub", diff --git a/tests/e2e/kserve_test.go b/tests/e2e/kserve_test.go index 715cc6e81f9..3fec9048f9d 100644 --- a/tests/e2e/kserve_test.go +++ b/tests/e2e/kserve_test.go @@ -2,16 +2,21 @@ package e2e_test import ( "encoding/json" + "errors" "fmt" "testing" + "github.com/rs/xid" "github.com/stretchr/testify/require" k8serr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" componentApi "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1alpha1" + featuresv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/features/v1" "github.com/opendatahub-io/opendatahub-operator/v2/controllers/components/modelcontroller" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk" @@ -31,18 +36,18 @@ func kserveTestSuite(t *testing.T) { ComponentTestCtx: ct, } - // TODO: removed once we know what's left on the cluster that's causing the tests - // to fail because of "existing KNativeServing resource was found" err = componentCtx.setUpServerless(t) require.NoError(t, err) t.Run("Validate component enabled", componentCtx.ValidateComponentEnabled) t.Run("Validate component spec", componentCtx.validateSpec) + t.Run("Validate FeatureTrackers", componentCtx.validateFeatureTrackers) t.Run("Validate model controller", componentCtx.validateModelControllerInstance) t.Run("Validate operands have OwnerReferences", componentCtx.ValidateOperandsOwnerReferences) t.Run("Validate default certs", componentCtx.validateDefaultCertsAvailable) t.Run("Validate update operand resources", componentCtx.ValidateUpdateDeploymentsResources) t.Run("Validate component disabled", componentCtx.ValidateComponentDisabled) + t.Run("Validate component releases", componentCtx.ValidateComponentReleases) } type KserveTestCtx struct { @@ -51,6 +56,8 @@ type KserveTestCtx struct { //nolint:thelper func (c *KserveTestCtx) setUpServerless(t *testing.T) error { + // TODO: removed once we know what's left on the cluster that's causing the tests + // to fail because of "existing KNativeServing resource was found" ksl := unstructured.UnstructuredList{} ksl.SetGroupVersionKind(gvk.KnativeServing) @@ -75,6 +82,24 @@ func (c *KserveTestCtx) setUpServerless(t *testing.T) error { } } + ft := &featuresv1.FeatureTracker{} + ft.SetName(c.ApplicationNamespace + "-serverless-serving-deployment") + + if _, err := controllerutil.CreateOrUpdate(c.Context(), c.Client(), ft, func() error { + dsc, err := c.GetDSC() + if err != nil { + return err + } + if err := controllerutil.SetOwnerReference(dsc, ft, c.Client().Scheme()); err != nil { + return err + } + ft.Spec.Source.Name = xid.New().String() + + return nil + }); err != nil { + return errors.New("error creating pre-existing FeatureTracker") + } + return nil } @@ -96,6 +121,49 @@ func (c *KserveTestCtx) validateSpec(t *testing.T) { )) } +func (c *KserveTestCtx) validateFeatureTrackers(t *testing.T) { + g := c.NewWithT(t) + ftName := types.NamespacedName{Name: c.ApplicationNamespace + "-serverless-serving-deployment"} + + g.Get(gvk.FeatureTracker, ftName).Eventually().Should(And( + jq.Match(`(.metadata.ownerReferences | length) == 1`), + jq.Match(`.metadata.ownerReferences[0].apiVersion == "%s"`, gvk.Kserve.GroupVersion().String()), + jq.Match(`.metadata.ownerReferences[0].kind == "%s"`, gvk.Kserve.Kind), + jq.Match(`.metadata.ownerReferences[0].blockOwnerDeletion == true`), + jq.Match(`.metadata.ownerReferences[0].controller == true`), + )) + + dsc, err := c.GetDSC() + g.Expect(err).NotTo(HaveOccurred()) + + g.Update( + gvk.FeatureTracker, + ftName, + func(obj *unstructured.Unstructured) error { + if err := controllerutil.SetOwnerReference(dsc, obj, c.Client().Scheme()); err != nil { + return err + } + + // trigger reconciliation as spec changes + if err = unstructured.SetNestedField(obj.Object, xid.New().String(), "spec", "source", "name"); err != nil { + return err + } + + return nil + }, + ).Eventually().Should(And( + jq.Match(`(.metadata.ownerReferences | length) == 2`), + )) + + g.Get(gvk.FeatureTracker, ftName).Eventually().Should(And( + jq.Match(`(.metadata.ownerReferences | length) == 1`), + jq.Match(`.metadata.ownerReferences[0].apiVersion == "%s"`, gvk.Kserve.GroupVersion().String()), + jq.Match(`.metadata.ownerReferences[0].kind == "%s"`, gvk.Kserve.Kind), + jq.Match(`.metadata.ownerReferences[0].blockOwnerDeletion == true`), + jq.Match(`.metadata.ownerReferences[0].controller == true`), + )) +} + func (c *KserveTestCtx) validateModelControllerInstance(t *testing.T) { g := c.NewWithT(t) diff --git a/tests/e2e/kueue_test.go b/tests/e2e/kueue_test.go index 4fb42093059..1cddd874a64 100644 --- a/tests/e2e/kueue_test.go +++ b/tests/e2e/kueue_test.go @@ -32,6 +32,7 @@ func kueueTestSuite(t *testing.T) { t.Run("Validate Kueue Dynamically create VAP and VAPB", componentCtx.validateKueueVAPReady) t.Run("Validate CRDs reinstated", componentCtx.validateCRDReinstated) t.Run("Validate component disabled", componentCtx.ValidateComponentDisabled) + t.Run("Validate component releases", componentCtx.ValidateComponentReleases) } type KueueTestCtx struct { diff --git a/tests/e2e/modelmeshserving_test.go b/tests/e2e/modelmeshserving_test.go index 005db8ff03a..7a723f3b1cd 100644 --- a/tests/e2e/modelmeshserving_test.go +++ b/tests/e2e/modelmeshserving_test.go @@ -29,6 +29,7 @@ func modelMeshServingTestSuite(t *testing.T) { t.Run("Validate operands have OwnerReferences", componentCtx.ValidateOperandsOwnerReferences) t.Run("Validate update operand resources", componentCtx.ValidateUpdateDeploymentsResources) t.Run("Validate component disabled", componentCtx.ValidateComponentDisabled) + t.Run("Validate component releases", componentCtx.ValidateComponentReleases) } type ModelMeshServingTestCtx struct { diff --git a/tests/e2e/modelregistry_test.go b/tests/e2e/modelregistry_test.go index 413226ef94a..e5241e70819 100644 --- a/tests/e2e/modelregistry_test.go +++ b/tests/e2e/modelregistry_test.go @@ -48,6 +48,7 @@ func modelRegistryTestSuite(t *testing.T) { t.Run("Validate ServiceMeshMember", componentCtx.validateModelRegistryServiceMeshMember) t.Run("Validate component disabled", componentCtx.ValidateComponentDisabled) + t.Run("Validate component releases", componentCtx.ValidateComponentReleases) } func (c *ModelRegistryTestCtx) validateSpec(t *testing.T) { diff --git a/tests/e2e/ray_test.go b/tests/e2e/ray_test.go index d56b0309c61..f666d6f25c7 100644 --- a/tests/e2e/ray_test.go +++ b/tests/e2e/ray_test.go @@ -22,6 +22,7 @@ func rayTestSuite(t *testing.T) { t.Run("Validate operands have OwnerReferences", componentCtx.ValidateOperandsOwnerReferences) t.Run("Validate update operand resources", componentCtx.ValidateUpdateDeploymentsResources) t.Run("Validate component disabled", componentCtx.ValidateComponentDisabled) + t.Run("Validate component releases", componentCtx.ValidateComponentReleases) } type RayTestCtx struct { diff --git a/tests/e2e/trainingoperator_test.go b/tests/e2e/trainingoperator_test.go index d1b4eca9367..823db746e5f 100644 --- a/tests/e2e/trainingoperator_test.go +++ b/tests/e2e/trainingoperator_test.go @@ -22,6 +22,7 @@ func trainingOperatorTestSuite(t *testing.T) { t.Run("Validate operands have OwnerReferences", componentCtx.ValidateOperandsOwnerReferences) t.Run("Validate update operand resources", componentCtx.ValidateUpdateDeploymentsResources) t.Run("Validate component disabled", componentCtx.ValidateComponentDisabled) + t.Run("Validate component releases", componentCtx.ValidateComponentReleases) } type TrainingOperatorTestCtx struct { diff --git a/tests/e2e/trustyai_test.go b/tests/e2e/trustyai_test.go index 7d9b4d9094f..2ddb58ec83b 100644 --- a/tests/e2e/trustyai_test.go +++ b/tests/e2e/trustyai_test.go @@ -22,6 +22,7 @@ func trustyAITestSuite(t *testing.T) { t.Run("Validate operands have OwnerReferences", componentCtx.ValidateOperandsOwnerReferences) t.Run("Validate update operand resources", componentCtx.ValidateUpdateDeploymentsResources) t.Run("Validate component disabled", componentCtx.ValidateComponentDisabled) + t.Run("Validate component releases", componentCtx.ValidateComponentReleases) } type TrustyAITestCtx struct {