From c13a9f3a7c19f5912567e9484b507b0f54c28b47 Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Tue, 12 Nov 2024 18:10:51 +0200 Subject: [PATCH] components: provide common interface Decouple components and main code of operator with ComponentHandler interface. It allows to hanle all common operations in a unique way and add components without changing main/dsc code. At the moment they are initialization, controller creation and fetching some component specific values (name, management status and corresponding CR). For the latter the code basically copy'n'paste unfortunately. Use explicit Init() functionality to register components. Automatic init() does not work since it requires package to be imported to be executed (without explicit call there is no need to import the packages) Non-recommended approach (and linter silencing) of returning interface is used to return component specific CR as common client.Object to simplify the code in DSC reconciler. Otherwise one more level of inderection is needed with passing required functionality (closure) to the interface function. Signed-off-by: Yauheni Kaliuta --- controllers/components/dashboard/dashboard.go | 24 ++++- .../dashboard/dashboard_controller.go | 2 +- .../datasciencepipelines.go | 24 ++++- .../datasciencepipelines_controller.go | 2 +- controllers/components/kueue/kueue.go | 26 ++++-- .../components/kueue/kueue_controller.go | 2 +- .../components/modelregistry/modelregistry.go | 24 ++++- .../modelregistry/modelregistry_controller.go | 3 +- controllers/components/ray/ray.go | 26 ++++-- controllers/components/ray/ray_controller.go | 2 +- .../trainingoperator/trainingoperator.go | 26 ++++-- .../trainingoperator_controller.go | 2 +- controllers/components/trustyai/trustyai.go | 26 ++++-- .../trustyai/trustyai_controller.go | 2 +- .../datasciencecluster_controller.go | 92 ++++--------------- main.go | 74 ++++----------- pkg/componentsregistry/componentsregistry.go | 49 ++++++++++ 17 files changed, 229 insertions(+), 177 deletions(-) create mode 100644 pkg/componentsregistry/componentsregistry.go diff --git a/controllers/components/dashboard/dashboard.go b/controllers/components/dashboard/dashboard.go index 6293e7d3908..4311e07f5c8 100644 --- a/controllers/components/dashboard/dashboard.go +++ b/controllers/components/dashboard/dashboard.go @@ -5,15 +5,31 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" componentsv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" + cr "github.com/opendatahub-io/opendatahub-operator/v2/pkg/componentsregistry" odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/annotations" ) -func Init(platform cluster.Platform) error { +type componentHandler struct{} + +func Init() { + cr.Add(&componentHandler{}) +} + +func (s *componentHandler) GetName() string { + return componentsv1.DashboardComponentName +} + +func (s *componentHandler) GetManagementState(dsc *dscv1.DataScienceCluster) operatorv1.ManagementState { + return dsc.Spec.Components.Dashboard.ManagementState +} + +func (s *componentHandler) Init(platform cluster.Platform) error { mi := defaultManifestInfo(platform) if err := odhdeploy.ApplyParams(mi.String(), imagesMap); err != nil { @@ -23,7 +39,7 @@ func Init(platform cluster.Platform) error { return nil } -func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.Dashboard { +func (s *componentHandler) NewCRObject(dsc *dscv1.DataScienceCluster) client.Object { //nolint:ireturn dashboardAnnotations := make(map[string]string) switch dsc.Spec.Components.Dashboard.ManagementState { @@ -33,7 +49,7 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.Dashboard { dashboardAnnotations[annotations.ManagementStateAnnotation] = "Unknown" } - return &componentsv1.Dashboard{ + return client.Object(&componentsv1.Dashboard{ TypeMeta: metav1.TypeMeta{ Kind: componentsv1.DashboardKind, APIVersion: componentsv1.GroupVersion.String(), @@ -45,5 +61,5 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.Dashboard { Spec: componentsv1.DashboardSpec{ DashboardCommonSpec: dsc.Spec.Components.Dashboard.DashboardCommonSpec, }, - } + }) } diff --git a/controllers/components/dashboard/dashboard_controller.go b/controllers/components/dashboard/dashboard_controller.go index 0600f689577..b2d1841357a 100644 --- a/controllers/components/dashboard/dashboard_controller.go +++ b/controllers/components/dashboard/dashboard_controller.go @@ -41,7 +41,7 @@ import ( ) // NewComponentReconciler creates a ComponentReconciler for the Dashboard API. -func NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { +func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { componentName := computeComponentName() _, err := reconciler.ComponentReconcilerFor(mgr, componentsv1.DashboardInstanceName, &componentsv1.Dashboard{}). diff --git a/controllers/components/datasciencepipelines/datasciencepipelines.go b/controllers/components/datasciencepipelines/datasciencepipelines.go index 3fef4322abe..0e0981f746d 100644 --- a/controllers/components/datasciencepipelines/datasciencepipelines.go +++ b/controllers/components/datasciencepipelines/datasciencepipelines.go @@ -5,10 +5,12 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" componentsv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" + cr "github.com/opendatahub-io/opendatahub-operator/v2/pkg/componentsregistry" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/annotations" ) @@ -17,7 +19,21 @@ const ( ArgoWorkflowCRD = "workflows.argoproj.io" ) -func Init(platform cluster.Platform) error { +type componentHandler struct{} + +func Init() { + cr.Add(&componentHandler{}) +} + +func (s *componentHandler) GetName() string { + return componentsv1.DashboardComponentName +} + +func (s *componentHandler) GetManagementState(dsc *dscv1.DataScienceCluster) operatorv1.ManagementState { + return dsc.Spec.Components.Dashboard.ManagementState +} + +func (s *componentHandler) Init(platform cluster.Platform) error { var imageParamMap = map[string]string{ // v1 "IMAGES_APISERVER": "RELATED_IMAGE_ODH_ML_PIPELINES_API_SERVER_IMAGE", @@ -44,7 +60,7 @@ func Init(platform cluster.Platform) error { return nil } -func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.DataSciencePipelines { +func (s *componentHandler) NewCRObject(dsc *dscv1.DataScienceCluster) client.Object { //nolint:ireturn dataSciencePipelinesAnnotations := make(map[string]string) switch dsc.Spec.Components.DataSciencePipelines.ManagementState { @@ -54,7 +70,7 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.DataSciencePipe dataSciencePipelinesAnnotations[annotations.ManagementStateAnnotation] = "Unknown" } - return &componentsv1.DataSciencePipelines{ + return client.Object(&componentsv1.DataSciencePipelines{ TypeMeta: metav1.TypeMeta{ Kind: componentsv1.DataSciencePipelinesKind, APIVersion: componentsv1.GroupVersion.String(), @@ -66,5 +82,5 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.DataSciencePipe Spec: componentsv1.DataSciencePipelinesSpec{ DataSciencePipelinesCommonSpec: dsc.Spec.Components.DataSciencePipelines.DataSciencePipelinesCommonSpec, }, - } + }) } diff --git a/controllers/components/datasciencepipelines/datasciencepipelines_controller.go b/controllers/components/datasciencepipelines/datasciencepipelines_controller.go index e9b085963ea..f35e40d7514 100644 --- a/controllers/components/datasciencepipelines/datasciencepipelines_controller.go +++ b/controllers/components/datasciencepipelines/datasciencepipelines_controller.go @@ -48,7 +48,7 @@ var ( } ) -func NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { +func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { _, err := reconciler.ComponentReconcilerFor( mgr, componentsv1.DataSciencePipelinesInstanceName, diff --git a/controllers/components/kueue/kueue.go b/controllers/components/kueue/kueue.go index bf0da0a024a..c8c333fc9b7 100644 --- a/controllers/components/kueue/kueue.go +++ b/controllers/components/kueue/kueue.go @@ -5,10 +5,12 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" componentsv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" + cr "github.com/opendatahub-io/opendatahub-operator/v2/pkg/componentsregistry" odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/annotations" ) @@ -21,8 +23,21 @@ var ( DefaultPath = odhdeploy.DefaultManifestPath + "/" + ComponentName + "/rhoai" // same path for both odh and rhoai ) -// for DSC to get compoment Kueue's CR. -func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.Kueue { +type componentHandler struct{} + +func Init() { + cr.Add(&componentHandler{}) +} + +func (s *componentHandler) GetName() string { + return componentsv1.KueueComponentName +} + +func (s *componentHandler) GetManagementState(dsc *dscv1.DataScienceCluster) operatorv1.ManagementState { + return dsc.Spec.Components.Kueue.ManagementState +} + +func (s *componentHandler) NewCRObject(dsc *dscv1.DataScienceCluster) client.Object { //nolint:ireturn kueueAnnotations := make(map[string]string) switch dsc.Spec.Components.Kueue.ManagementState { case operatorv1.Managed, operatorv1.Removed: @@ -31,7 +46,7 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.Kueue { kueueAnnotations[annotations.ManagementStateAnnotation] = "Unknown" } - return &componentsv1.Kueue{ + return client.Object(&componentsv1.Kueue{ TypeMeta: metav1.TypeMeta{ Kind: componentsv1.KueueKind, APIVersion: componentsv1.GroupVersion.String(), @@ -43,11 +58,10 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.Kueue { Spec: componentsv1.KueueSpec{ KueueCommonSpec: dsc.Spec.Components.Kueue.KueueCommonSpec, }, - } + }) } -// Init for set images. -func Init(platform cluster.Platform) error { +func (s *componentHandler) Init(platform cluster.Platform) error { imageParamMap := map[string]string{ "odh-kueue-controller-image": "RELATED_IMAGE_ODH_KUEUE_CONTROLLER_IMAGE", } diff --git a/controllers/components/kueue/kueue_controller.go b/controllers/components/kueue/kueue_controller.go index 1355f641a1f..32b0356381d 100644 --- a/controllers/components/kueue/kueue_controller.go +++ b/controllers/components/kueue/kueue_controller.go @@ -39,7 +39,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels" ) -func NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { +func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { _, err := reconciler.ComponentReconcilerFor( mgr, componentsv1.KueueInstanceName, diff --git a/controllers/components/modelregistry/modelregistry.go b/controllers/components/modelregistry/modelregistry.go index cc9786a896f..ee6e714d0e1 100644 --- a/controllers/components/modelregistry/modelregistry.go +++ b/controllers/components/modelregistry/modelregistry.go @@ -5,15 +5,31 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" componentsv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" + cr "github.com/opendatahub-io/opendatahub-operator/v2/pkg/componentsregistry" odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/annotations" ) -func Init(_ cluster.Platform) error { +type componentHandler struct{} + +func Init() { + cr.Add(&componentHandler{}) +} + +func (s *componentHandler) GetName() string { + return componentsv1.ModelRegistryComponentName +} + +func (s *componentHandler) GetManagementState(dsc *dscv1.DataScienceCluster) operatorv1.ManagementState { + return dsc.Spec.Components.ModelRegistry.ManagementState +} + +func (s *componentHandler) Init(_ cluster.Platform) error { mi := baseManifestInfo(BaseManifestsSourcePath) params := make(map[string]string) @@ -31,7 +47,7 @@ func Init(_ cluster.Platform) error { return nil } -func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.ModelRegistry { +func (s *componentHandler) NewCRObject(dsc *dscv1.DataScienceCluster) client.Object { //nolint:ireturn componentAnnotations := make(map[string]string) switch dsc.Spec.Components.ModelRegistry.ManagementState { @@ -42,7 +58,7 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.ModelRegistry { componentAnnotations[annotations.ManagementStateAnnotation] = "Unknown" } - return &componentsv1.ModelRegistry{ + return client.Object(&componentsv1.ModelRegistry{ TypeMeta: metav1.TypeMeta{ Kind: componentsv1.ModelRegistryKind, APIVersion: componentsv1.GroupVersion.String(), @@ -54,5 +70,5 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.ModelRegistry { Spec: componentsv1.ModelRegistrySpec{ ModelRegistryCommonSpec: dsc.Spec.Components.ModelRegistry.ModelRegistryCommonSpec, }, - } + }) } diff --git a/controllers/components/modelregistry/modelregistry_controller.go b/controllers/components/modelregistry/modelregistry_controller.go index fe8ba797bf9..3b9fb9dcfec 100644 --- a/controllers/components/modelregistry/modelregistry_controller.go +++ b/controllers/components/modelregistry/modelregistry_controller.go @@ -39,8 +39,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels" ) -// NewComponentReconciler creates a ComponentReconciler for the Dashboard API. -func NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { +func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { _, err := reconciler.ComponentReconcilerFor( mgr, componentsv1.ModelRegistryInstanceName, diff --git a/controllers/components/ray/ray.go b/controllers/components/ray/ray.go index 5b7bfb03fed..2f7ba48951c 100644 --- a/controllers/components/ray/ray.go +++ b/controllers/components/ray/ray.go @@ -5,10 +5,12 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" componentsv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" + cr "github.com/opendatahub-io/opendatahub-operator/v2/pkg/componentsregistry" odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/annotations" ) @@ -21,8 +23,21 @@ var ( DefaultPath = odhdeploy.DefaultManifestPath + "/" + ComponentName + "/openshift" ) -// for DSC to get compoment Ray's CR. -func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.Ray { +type componentHandler struct{} + +func Init() { + cr.Add(&componentHandler{}) +} + +func (s *componentHandler) GetName() string { + return componentsv1.RayComponentName +} + +func (s *componentHandler) GetManagementState(dsc *dscv1.DataScienceCluster) operatorv1.ManagementState { + return dsc.Spec.Components.Ray.ManagementState +} + +func (s *componentHandler) NewCRObject(dsc *dscv1.DataScienceCluster) client.Object { //nolint:ireturn rayAnnotations := make(map[string]string) switch dsc.Spec.Components.Ray.ManagementState { case operatorv1.Managed, operatorv1.Removed: @@ -31,7 +46,7 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.Ray { rayAnnotations[annotations.ManagementStateAnnotation] = "Unknown" } - return &componentsv1.Ray{ + return client.Object(&componentsv1.Ray{ TypeMeta: metav1.TypeMeta{ Kind: componentsv1.RayKind, APIVersion: componentsv1.GroupVersion.String(), @@ -43,11 +58,10 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.Ray { Spec: componentsv1.RaySpec{ RayCommonSpec: dsc.Spec.Components.Ray.RayCommonSpec, }, - } + }) } -// Init for set images. -func Init(platform cluster.Platform) error { +func (s *componentHandler) Init(platform cluster.Platform) error { imageParamMap := map[string]string{ "odh-kuberay-operator-controller-image": "RELATED_IMAGE_ODH_KUBERAY_OPERATOR_CONTROLLER_IMAGE", } diff --git a/controllers/components/ray/ray_controller.go b/controllers/components/ray/ray_controller.go index 83781ace84c..bc6efc23115 100644 --- a/controllers/components/ray/ray_controller.go +++ b/controllers/components/ray/ray_controller.go @@ -37,7 +37,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels" ) -func NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { +func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { _, err := reconciler.ComponentReconcilerFor( mgr, componentsv1.RayInstanceName, diff --git a/controllers/components/trainingoperator/trainingoperator.go b/controllers/components/trainingoperator/trainingoperator.go index 3c51fe8e525..5417c39743e 100644 --- a/controllers/components/trainingoperator/trainingoperator.go +++ b/controllers/components/trainingoperator/trainingoperator.go @@ -5,10 +5,12 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" componentsv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" + cr "github.com/opendatahub-io/opendatahub-operator/v2/pkg/componentsregistry" odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/annotations" ) @@ -21,8 +23,21 @@ var ( DefaultPath = odhdeploy.DefaultManifestPath + "/" + ComponentName + "/rhoai" ) -// for DSC to get compoment TrainingOperator's CR. -func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.TrainingOperator { +type componentHandler struct{} + +func Init() { + cr.Add(&componentHandler{}) +} + +func (s *componentHandler) GetName() string { + return componentsv1.TrainingOperatorComponentName +} + +func (s *componentHandler) GetManagementState(dsc *dscv1.DataScienceCluster) operatorv1.ManagementState { + return dsc.Spec.Components.TrainingOperator.ManagementState +} + +func (s *componentHandler) NewCRObject(dsc *dscv1.DataScienceCluster) k8sclient.Object { //nolint:ireturn trainingoperatorAnnotations := make(map[string]string) switch dsc.Spec.Components.TrainingOperator.ManagementState { case operatorv1.Managed, operatorv1.Removed: @@ -31,7 +46,7 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.TrainingOperato trainingoperatorAnnotations[annotations.ManagementStateAnnotation] = "Unknown" } - return &componentsv1.TrainingOperator{ + return k8sclient.Object(&componentsv1.TrainingOperator{ TypeMeta: metav1.TypeMeta{ Kind: componentsv1.TrainingOperatorKind, APIVersion: componentsv1.GroupVersion.String(), @@ -43,11 +58,10 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.TrainingOperato Spec: componentsv1.TrainingOperatorSpec{ TrainingOperatorCommonSpec: dsc.Spec.Components.TrainingOperator.TrainingOperatorCommonSpec, }, - } + }) } -// Init for set images. -func Init(platform cluster.Platform) error { +func (s *componentHandler) Init(platform cluster.Platform) error { imageParamMap := map[string]string{ "odh-training-operator-controller-image": "RELATED_IMAGE_ODH_TRAINING_OPERATOR_IMAGE", } diff --git a/controllers/components/trainingoperator/trainingoperator_controller.go b/controllers/components/trainingoperator/trainingoperator_controller.go index ba65e7e71ee..610c3bf0a4d 100644 --- a/controllers/components/trainingoperator/trainingoperator_controller.go +++ b/controllers/components/trainingoperator/trainingoperator_controller.go @@ -37,7 +37,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels" ) -func NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { +func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { _, err := reconciler.ComponentReconcilerFor( mgr, componentsv1.TrainingOperatorInstanceName, diff --git a/controllers/components/trustyai/trustyai.go b/controllers/components/trustyai/trustyai.go index a166863e3e3..a683bfdb02f 100644 --- a/controllers/components/trustyai/trustyai.go +++ b/controllers/components/trustyai/trustyai.go @@ -5,10 +5,12 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" componentsv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" + cr "github.com/opendatahub-io/opendatahub-operator/v2/pkg/componentsregistry" odhdeploy "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/annotations" ) @@ -27,8 +29,21 @@ var ( } ) -// for DSC to get compoment TrustyAI's CR. -func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.TrustyAI { +type componentHandler struct{} + +func Init() { + cr.Add(&componentHandler{}) +} + +func (s *componentHandler) GetName() string { + return componentsv1.TrustyAIComponentName +} + +func (s *componentHandler) GetManagementState(dsc *dscv1.DataScienceCluster) operatorv1.ManagementState { + return dsc.Spec.Components.TrustyAI.ManagementState +} + +func (s *componentHandler) NewCRObject(dsc *dscv1.DataScienceCluster) client.Object { //nolint:ireturn trustyaiAnnotations := make(map[string]string) switch dsc.Spec.Components.TrustyAI.ManagementState { case operatorv1.Managed, operatorv1.Removed: @@ -37,7 +52,7 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.TrustyAI { trustyaiAnnotations[annotations.ManagementStateAnnotation] = "Unknown" } - return &componentsv1.TrustyAI{ + return client.Object(&componentsv1.TrustyAI{ TypeMeta: metav1.TypeMeta{ Kind: componentsv1.TrustyAIKind, APIVersion: componentsv1.GroupVersion.String(), @@ -49,11 +64,10 @@ func GetComponentCR(dsc *dscv1.DataScienceCluster) *componentsv1.TrustyAI { Spec: componentsv1.TrustyAISpec{ TrustyAICommonSpec: dsc.Spec.Components.TrustyAI.TrustyAICommonSpec, }, - } + }) } -// Init for set images. -func Init(platform cluster.Platform) error { +func (s *componentHandler) Init(platform cluster.Platform) error { imageParamMap := map[string]string{ "trustyaiServiceImage": "RELATED_IMAGE_ODH_TRUSTYAI_SERVICE_IMAGE", "trustyaiOperatorImage": "RELATED_IMAGE_ODH_TRUSTYAI_SERVICE_OPERATOR_IMAGE", diff --git a/controllers/components/trustyai/trustyai_controller.go b/controllers/components/trustyai/trustyai_controller.go index 82ebfd6461d..85e602d6d85 100644 --- a/controllers/components/trustyai/trustyai_controller.go +++ b/controllers/components/trustyai/trustyai_controller.go @@ -36,7 +36,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels" ) -func NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { +func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { _, err := reconciler.ComponentReconcilerFor( mgr, componentsv1.TrustyAIInstanceName, diff --git a/controllers/datasciencecluster/datasciencecluster_controller.go b/controllers/datasciencecluster/datasciencecluster_controller.go index 494a69c8fda..649ac52bd8a 100644 --- a/controllers/datasciencecluster/datasciencecluster_controller.go +++ b/controllers/datasciencecluster/datasciencecluster_controller.go @@ -25,7 +25,6 @@ import ( "time" "github.com/go-logr/logr" - "github.com/hashicorp/go-multierror" buildv1 "github.com/openshift/api/build/v1" imagev1 "github.com/openshift/api/image/v1" operatorv1 "github.com/openshift/api/operator/v1" @@ -52,15 +51,10 @@ import ( componentsv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/components/v1" dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" dsciv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/dscinitialization/v1" - dashboardctrl "github.com/opendatahub-io/opendatahub-operator/v2/controllers/components/dashboard" datasciencepipelinesctrl "github.com/opendatahub-io/opendatahub-operator/v2/controllers/components/datasciencepipelines" - kueuectrl "github.com/opendatahub-io/opendatahub-operator/v2/controllers/components/kueue" - modelregistryctrl "github.com/opendatahub-io/opendatahub-operator/v2/controllers/components/modelregistry" - rayctrl "github.com/opendatahub-io/opendatahub-operator/v2/controllers/components/ray" - trainingoperatorctrl "github.com/opendatahub-io/opendatahub-operator/v2/controllers/components/trainingoperator" - trustyaictrl "github.com/opendatahub-io/opendatahub-operator/v2/controllers/components/trustyai" "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" odhClient "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/client" annotations "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/annotations" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels" @@ -88,7 +82,7 @@ const ( // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. -func (r *DataScienceClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { //nolint:maintidx,gocyclo +func (r *DataScienceClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { //nolint:maintidx log := r.Log log.Info("Reconciling DataScienceCluster resources", "Request.Name", req.Name) @@ -228,66 +222,11 @@ func (r *DataScienceClusterReconciler) Reconcile(ctx context.Context, req ctrl.R } } - // Initialize error list, instead of returning errors after every component is deployed - var componentErrors *multierror.Error - - // Deploy Dashboard - if instance, err = r.ReconcileComponent(ctx, instance, componentsv1.DashboardComponentName, func() (error, bool) { - // Get the Dashboard instance - dashboard := dashboardctrl.GetComponentCR(instance) - // Reconcile component either create CR with setting owner or delete it - return r.apply(ctx, instance, dashboard), instance.Spec.Components.Dashboard.ManagementState == operatorv1.Managed - }); err != nil { - componentErrors = multierror.Append(componentErrors, err) - } - - // Deploy Ray - if instance, err = r.ReconcileComponent(ctx, instance, componentsv1.RayComponentName, func() (error, bool) { - ray := rayctrl.GetComponentCR(instance) - return r.apply(ctx, instance, ray), instance.Spec.Components.Ray.ManagementState == operatorv1.Managed - }); err != nil { - componentErrors = multierror.Append(componentErrors, err) - } - - // Deploy Model Registry - if instance, err = r.ReconcileComponent(ctx, instance, componentsv1.ModelRegistryComponentName, func() (error, bool) { - modelregistry := modelregistryctrl.GetComponentCR(instance) - return r.apply(ctx, instance, modelregistry), instance.Spec.Components.ModelRegistry.ManagementState == operatorv1.Managed - }); err != nil { - componentErrors = multierror.Append(componentErrors, err) - } - - // Deploy TrustyAI - if instance, err = r.ReconcileComponent(ctx, instance, componentsv1.TrustyAIComponentName, func() (error, bool) { - trustyai := trustyaictrl.GetComponentCR(instance) - return r.apply(ctx, instance, trustyai), instance.Spec.Components.TrustyAI.ManagementState == operatorv1.Managed - }); err != nil { - componentErrors = multierror.Append(componentErrors, err) - } - - // Deploy Kueue - if instance, err = r.ReconcileComponent(ctx, instance, componentsv1.KueueComponentName, func() (error, bool) { - kueue := kueuectrl.GetComponentCR(instance) - return r.apply(ctx, instance, kueue), instance.Spec.Components.Kueue.ManagementState == operatorv1.Managed - }); err != nil { - componentErrors = multierror.Append(componentErrors, err) - } - - // Deploy TrainingOperator - if instance, err = r.ReconcileComponent(ctx, instance, componentsv1.TrainingOperatorComponentName, func() (error, bool) { - trainingoperator := trainingoperatorctrl.GetComponentCR(instance) - return r.apply(ctx, instance, trainingoperator), instance.Spec.Components.TrainingOperator.ManagementState == operatorv1.Managed - }); err != nil { - componentErrors = multierror.Append(componentErrors, err) - } - - // Deploy DataSciencePipelines - if instance, err = r.ReconcileComponent(ctx, instance, componentsv1.DataSciencePipelinesComponentName, func() (error, bool) { - dsp := datasciencepipelinesctrl.GetComponentCR(instance) - return r.apply(ctx, instance, dsp), instance.Spec.Components.DataSciencePipelines.ManagementState == operatorv1.Managed - }); err != nil { - componentErrors = multierror.Append(componentErrors, err) - } + componentErrors := cr.ForEach(func(component cr.ComponentHandler) error { + var err error + instance, err = r.ReconcileComponent(ctx, instance, component) + return err + }) // Process errors for components if componentErrors != nil { @@ -329,20 +268,23 @@ func (r *DataScienceClusterReconciler) Reconcile(ctx context.Context, req ctrl.R return ctrl.Result{}, nil } -type ComponentHandler func() (error, bool) - -// TODO: make it generic for all components. func (r *DataScienceClusterReconciler) ReconcileComponent( ctx context.Context, instance *dscv1.DataScienceCluster, - componentName string, - componentRec ComponentHandler, + component cr.ComponentHandler, ) (*dscv1.DataScienceCluster, error) { + componentName := component.GetName() + r.Log.Info("Starting reconciliation of component: " + componentName) - err, enabled := componentRec() - _, isExistStatus := instance.Status.InstalledComponents[componentName] + enabled := component.GetManagementState(instance) == operatorv1.Managed + componentCR := component.NewCRObject(instance) + err := r.apply(ctx, instance, componentCR) + if err != nil { + return instance, err + } + _, isExistStatus := instance.Status.InstalledComponents[componentName] if !isExistStatus { message := "Component is disabled" if enabled { diff --git a/main.go b/main.go index 16e61f416c2..cd73bccd24e 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,6 @@ import ( "flag" "os" - "github.com/hashicorp/go-multierror" addonv1alpha1 "github.com/openshift/addon-operator/apis/addons/v1alpha1" ocappsv1 "github.com/openshift/api/apps/v1" //nolint:importas //reason: conflicts with appsv1 "k8s.io/api/apps/v1" buildv1 "github.com/openshift/api/build/v1" @@ -76,6 +75,7 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/controllers/webhook" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk" + cr "github.com/opendatahub-io/opendatahub-operator/v2/pkg/componentsregistry" odhClient "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/client" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/logger" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/resources" @@ -119,31 +119,17 @@ func init() { //nolint:gochecknoinits } func initComponents(_ context.Context, p cluster.Platform) error { - var multiErr *multierror.Error - - if err := dashboardctrl.Init(p); err != nil { - multiErr = multierror.Append(multiErr, err) - } - if err := rayctrl.Init(p); err != nil { - multiErr = multierror.Append(multiErr, err) - } - if err := modelregistryctrl.Init(p); err != nil { - return err - } - if err := trainingoperatorctrl.Init(p); err != nil { - return err - } - if err := trustyaictrl.Init(p); err != nil { - return err - } - if err := datasciencepipelinesctrl.Init(p); err != nil { - multiErr = multierror.Append(multiErr, err) - } - - if err := kueuectrl.Init(p); err != nil { - multiErr = multierror.Append(multiErr, err) - } - return multiErr.ErrorOrNil() + dashboardctrl.Init() + rayctrl.Init() + modelregistryctrl.Init() + trainingoperatorctrl.Init() + trustyaictrl.Init() + kueuectrl.Init() + datasciencepipelinesctrl.Init() + + return cr.ForEach(func(ch cr.ComponentHandler) error { + return ch.Init(p) + }) } func main() { //nolint:funlen,maintidx @@ -436,36 +422,8 @@ func createDeploymentCacheConfig(platform cluster.Platform) map[string]cache.Con } func CreateComponentReconcilers(ctx context.Context, mgr manager.Manager) error { - // TODO: add more here or make it go routine - if err := dashboardctrl.NewComponentReconciler(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "DashboardReconciler") - return err - } - - if err := rayctrl.NewComponentReconciler(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "RayReconciler") - return err - } - if err := modelregistryctrl.NewComponentReconciler(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "ModelRegistryReconciler") - return err - } - if err := trustyaictrl.NewComponentReconciler(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "TrustyAIReconciler") - return err - } - if err := kueuectrl.NewComponentReconciler(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "KueueReconciler") - return err - } - if err := trainingoperatorctrl.NewComponentReconciler(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "TrainingOperatorReconciler") - return err - } - if err := datasciencepipelinesctrl.NewComponentReconciler(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "DataSciencePipelinesReconciler") - return err - } - - return nil + // TODO: can it be moved to initComponents? + return cr.ForEach(func(ch cr.ComponentHandler) error { + return ch.NewComponentReconciler(ctx, mgr) + }) } diff --git a/pkg/componentsregistry/componentsregistry.go b/pkg/componentsregistry/componentsregistry.go new file mode 100644 index 00000000000..c6591d1a218 --- /dev/null +++ b/pkg/componentsregistry/componentsregistry.go @@ -0,0 +1,49 @@ +// componentsregistry package is a registry of all components that can be managed by the operator +// TODO: it may make sense to put it under components/ when it's clear from the old stuff +package componentsregistry + +import ( + "context" + + "github.com/hashicorp/go-multierror" + operatorv1 "github.com/openshift/api/operator/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + dscv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/datasciencecluster/v1" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" +) + +// ComponentHandler is an interface to manage a component +// Every method should accept ctx since it contains the logger. +type ComponentHandler interface { + Init(platform cluster.Platform) error + // GetName and GetManagementState sound like pretty much the same across + // all components, but I could not find a way to avoid it + GetName() string + GetManagementState(dsc *dscv1.DataScienceCluster) operatorv1.ManagementState + // NewCRObject constructs components specific Custom Resource + // e.g. Dashboard in datasciencecluster.opendatahub.io group + // It returns interface, but it simplifies DSC reconciler code a lot + NewCRObject(dsc *dscv1.DataScienceCluster) client.Object + NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error +} + +var registry = []ComponentHandler{} + +// Add registers a new component handler +// not thread safe, supposed to be called during init. +// TODO: check if init() can be called in parallel. +func Add(ch ComponentHandler) { + registry = append(registry, ch) +} + +// ForEach iterates over all registered component handlers +// With go1.23 probably https://go.dev/blog/range-functions can be used. +func ForEach(f func(ch ComponentHandler) error) error { + var errs *multierror.Error + for _, ch := range registry { + errs = multierror.Append(errs, f(ch)) + } + return errs.ErrorOrNil() +}