diff --git a/controllers/datasciencecluster/datasciencecluster_controller.go b/controllers/datasciencecluster/datasciencecluster_controller.go index 3406186344b..d303eb709b4 100644 --- a/controllers/datasciencecluster/datasciencecluster_controller.go +++ b/controllers/datasciencecluster/datasciencecluster_controller.go @@ -19,6 +19,10 @@ package datasciencecluster import ( "context" "fmt" + dsci "github.com/opendatahub-io/opendatahub-operator/v2/apis/dscinitialization/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" "time" "github.com/go-logr/logr" @@ -115,6 +119,36 @@ func (r *DataScienceClusterReconciler) Reconcile(ctx context.Context, req ctrl.R } } + // Verify a valid DSCInitialization instance is created + dsciInstances := &dsci.DSCInitializationList{} + err = r.Client.List(ctx, dsciInstances) + if err != nil { + r.Log.Error(err, "Failed to retrieve DSCInitialization resource.", "DSCInitialization", req.Namespace, "Request.Name", req.Name) + r.Recorder.Eventf(instance, corev1.EventTypeWarning, "DSCInitializationReconcileError", "Failed to retrieve DSCInitialization instance") + return ctrl.Result{}, err + } + + // Update phase to error state if DataScienceCluster is created without valid DSCInitialization + if len(dsciInstances.Items) == 0 { + reason := status.ReconcileFailed + message := "Failed to get a valid DSCInitialization instance" + instance, err = r.updateStatus(instance, func(saved *dsc.DataScienceCluster) { + status.SetProgressingCondition(&saved.Status.Conditions, reason, message) + saved.Status.Phase = status.PhaseError + }) + if err != nil { + r.Log.Error(err, "failed to update DataScienceCluster condition") + return ctrl.Result{}, err + } else { + return ctrl.Result{}, nil + } + } else if len(dsciInstances.Items) == 1 { + // Set Applications namespace defined in DSCInitialization + r.ApplicationsNamespace = dsciInstances.Items[0].Spec.ApplicationsNamespace + } else { + return ctrl.Result{}, fmt.Errorf(fmt.Sprintf("only one instance of DSCInitialization object is allowed.")) + } + // Ensure all omitted components show up as explicitly disabled instance, err = r.updateComponents(instance) if err != nil { @@ -282,6 +316,7 @@ func (r *DataScienceClusterReconciler) SetupWithManager(mgr ctrl.Manager) error Owns(&appsv1.Deployment{}). Owns(&appsv1.ReplicaSet{}). Owns(&corev1.Pod{}). + Watches(&source.Kind{Type: &dsci.DSCInitialization{}}, handler.EnqueueRequestsFromMapFunc(r.watchDataScienceClusterResources)). // this predicates prevents meaningless reconciliations from being triggered WithEventFilter(predicate.Or(predicate.GenerationChangedPredicate{}, predicate.LabelChangedPredicate{})). Complete(r) @@ -324,3 +359,17 @@ func (r *DataScienceClusterReconciler) updateComponents(original *dsc.DataScienc }) return saved, err } + +func (r *DataScienceClusterReconciler) watchDataScienceClusterResources(a client.Object) (requests []reconcile.Request) { + instanceList := &dsc.DataScienceClusterList{} + err := r.Client.List(context.TODO(), instanceList) + if err != nil { + return nil + } + if len(instanceList.Items) == 1 { + return []reconcile.Request{{ + NamespacedName: types.NamespacedName{Name: instanceList.Items[0].Name}}} + } else { + return nil + } +} diff --git a/controllers/dscinitialization/dscinitialization_controller.go b/controllers/dscinitialization/dscinitialization_controller.go index 27868d32918..df369b48dbe 100644 --- a/controllers/dscinitialization/dscinitialization_controller.go +++ b/controllers/dscinitialization/dscinitialization_controller.go @@ -19,6 +19,7 @@ package dscinitialization import ( "context" "errors" + "fmt" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -71,11 +72,16 @@ func (r *DSCInitializationReconciler) Reconcile(ctx context.Context, req ctrl.Re r.Log.Info("Reconciling DSCInitialization.", "DSCInitialization", req.Namespace, "Request.Name", req.Name) instance := &dsci.DSCInitialization{} - // Only apply reconcile logic to 'default' instance of DataScienceInitialization - err := r.Client.Get(ctx, types.NamespacedName{Name: "default"}, instance) + // First check if instance is being deleted, return + if instance.GetDeletionTimestamp() != nil { + return ctrl.Result{}, nil + } + + // Second check if instance exists, return + err := r.Client.Get(context.TODO(), types.NamespacedName{Name: req.Name}, instance) if err != nil { if apierrs.IsNotFound(err) { - // DataScienceInitialization instance not found. + // DSCInitialization instance not found return ctrl.Result{}, nil } r.Log.Error(err, "Failed to retrieve DSCInitialization resource.", "DSCInitialization", req.Namespace, "Request.Name", req.Name) @@ -83,6 +89,18 @@ func (r *DSCInitializationReconciler) Reconcile(ctx context.Context, req ctrl.Re return ctrl.Result{}, err } + // Last check if multiple instances of DSCInitialization exist + instanceList := &dsci.DSCInitializationList{} + err = r.Client.List(context.TODO(), instanceList) + if err != nil { + return ctrl.Result{}, err + } + + if len(instanceList.Items) > 1 { + message := fmt.Sprintf("only one instance of DSCInitialization object is allowed. Update existing instance on namespace %s and name %s", req.Namespace, req.Name) + return ctrl.Result{}, fmt.Errorf(message) + } + // Start reconciling if instance.Status.Conditions == nil { reason := status.ReconcileInit diff --git a/main.go b/main.go index f5b17db3246..7ccfe087588 100644 --- a/main.go +++ b/main.go @@ -156,45 +156,48 @@ func main() { } //+kubebuilder:scaffold:builder - - // Create OCSInitialization CR if it's not present - client := mgr.GetClient() - releaseDscInitialization := &dsci.DSCInitialization{ - TypeMeta: metav1.TypeMeta{ - Kind: "DSCInitialization", - APIVersion: "dscinitialization.opendatahub.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - }, - Spec: dsci.DSCInitializationSpec{ - ApplicationsNamespace: dscApplicationsNamespace, - Monitoring: dsci.Monitoring{ - Enabled: false, + // Check if user opted for disabling DSC configuration + _, disableDSCConfig := os.LookupEnv("DISABLE_DSC_CONFIG") + if !disableDSCConfig { + // Create DSCInitialization CR if it's not present + client := mgr.GetClient() + releaseDscInitialization := &dsci.DSCInitialization{ + TypeMeta: metav1.TypeMeta{ + Kind: "DSCInitialization", + APIVersion: "dscinitialization.opendatahub.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + Spec: dsci.DSCInitializationSpec{ + ApplicationsNamespace: dscApplicationsNamespace, + Monitoring: dsci.Monitoring{ + Enabled: false, + }, }, - }, - } - err = client.Create(context.TODO(), releaseDscInitialization) - switch { - case err == nil: - setupLog.Info("created DscInitialization resource") - case errors.IsAlreadyExists(err): - // Update if already exists - setupLog.Info("DscInitialization resource already exists. Updating it.") - data, err := json.Marshal(releaseDscInitialization) - if err != nil { - setupLog.Error(err, "failed to get DscInitialization custom resource data") } - err = client.Patch(context.TODO(), releaseDscInitialization, client2.RawPatch(types.ApplyPatchType, data), - client2.ForceOwnership, client2.FieldOwner("opendatahub-operator")) - if err != nil { - setupLog.Error(err, "failed to update DscInitialization custom resource") + err = client.Create(context.TODO(), releaseDscInitialization) + switch { + case err == nil: + setupLog.Info("created DscInitialization resource") + case errors.IsAlreadyExists(err): + // Update if already exists + setupLog.Info("DscInitialization resource already exists. Updating it.") + data, err := json.Marshal(releaseDscInitialization) + if err != nil { + setupLog.Error(err, "failed to get DscInitialization custom resource data") + } + err = client.Patch(context.TODO(), releaseDscInitialization, client2.RawPatch(types.ApplyPatchType, data), + client2.ForceOwnership, client2.FieldOwner("opendatahub-operator")) + if err != nil { + setupLog.Error(err, "failed to update DscInitialization custom resource") + } + default: + setupLog.Error(err, "failed to create DscInitialization custom resource") + os.Exit(1) } - default: - setupLog.Error(err, "failed to create DscInitialization custom resource") - os.Exit(1) - } + } if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") os.Exit(1)