From 9a3361f2786a1917417f520834ea6e14bafa2952 Mon Sep 17 00:00:00 2001 From: Manohar Reddy Date: Thu, 11 Jun 2026 13:23:05 +0200 Subject: [PATCH] show error when cluster is not found in namespace --- api/v1alpha1/pool_types.go | 13 +++++++ api/v1alpha1/zz_generated.deepcopy.go | 7 ++++ .../controller/simplyblockpool_controller.go | 38 +++++++++++++++++-- internal/utils/objects.go | 7 +++- 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/api/v1alpha1/pool_types.go b/api/v1alpha1/pool_types.go index 0e907300..e31c5c5a 100644 --- a/api/v1alpha1/pool_types.go +++ b/api/v1alpha1/pool_types.go @@ -151,6 +151,12 @@ type PoolSpec struct { StorageClassParameters *StorageClassParameters `json:"storageClassParameters,omitempty"` } +// Condition type constants used in PoolStatus.Conditions. +const ( + // ConditionTypePoolReady indicates the pool is fully reconciled and operational. + ConditionTypePoolReady = "Ready" +) + // PoolStatus defines the observed state of Pool. type PoolStatus struct { // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Pool UUID" @@ -163,6 +169,13 @@ type PoolStatus struct { QoS *PoolQoSStatus `json:"qos,omitempty"` // AllowedNodes lists the Kubernetes node names currently registered on the backend. AllowedNodes []string `json:"allowedNodes,omitempty"` + // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Conditions" + // Conditions provides human-readable status conditions for kubectl describe output. + // +listType=map + // +listMapKey=type + // +patchStrategy=merge + // +patchMergeKey=type + Conditions []metav1.Condition `json:"conditions,omitempty"` } // +kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a8145879..4249134a 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -944,6 +944,13 @@ func (in *PoolStatus) DeepCopyInto(out *PoolStatus) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PoolStatus. diff --git a/internal/controller/simplyblockpool_controller.go b/internal/controller/simplyblockpool_controller.go index faa83c8d..c007b781 100644 --- a/internal/controller/simplyblockpool_controller.go +++ b/internal/controller/simplyblockpool_controller.go @@ -92,9 +92,11 @@ func (r *PoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. ) if err != nil { - log.Info("Cluster UUID not ready yet, requeuing", - "cluster", poolCR.Spec.ClusterName, - ) + log.Info("Cluster not ready, requeuing", "cluster", poolCR.Spec.ClusterName, "reason", err.Error()) + if setErr := r.setCondition(ctx, poolCR, simplyblockv1alpha1.ConditionTypePoolReady, + metav1.ConditionFalse, "ClusterNotFound", err.Error()); setErr != nil { + log.Error(setErr, "Failed to set Ready condition") + } return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } @@ -491,6 +493,36 @@ func (r *PoolReconciler) syncPoolHosts( return changed, nil } +// setCondition patches the Pool status with the given condition. +func (r *PoolReconciler) setCondition(ctx context.Context, poolCR *simplyblockv1alpha1.Pool, condType string, status metav1.ConditionStatus, reason, message string) error { + now := metav1.Now() + for i, c := range poolCR.Status.Conditions { + if c.Type == condType { + if c.Status == status && c.Reason == reason && c.Message == message { + return nil + } + poolCR.Status.Conditions[i] = metav1.Condition{ + Type: condType, + Status: status, + Reason: reason, + Message: message, + LastTransitionTime: now, + ObservedGeneration: poolCR.Generation, + } + return r.Status().Update(ctx, poolCR) + } + } + poolCR.Status.Conditions = append(poolCR.Status.Conditions, metav1.Condition{ + Type: condType, + Status: status, + Reason: reason, + Message: message, + LastTransitionTime: now, + ObservedGeneration: poolCR.Generation, + }) + return r.Status().Update(ctx, poolCR) +} + // SetupWithManager sets up the controller with the Manager. func (r *PoolReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/internal/utils/objects.go b/internal/utils/objects.go index 06aafc5a..e06f8ae2 100644 --- a/internal/utils/objects.go +++ b/internal/utils/objects.go @@ -96,12 +96,15 @@ func ResolveClusterUUID( } for _, cluster := range clusters.Items { - if cluster.Name == clusterName && cluster.Status.UUID != "" { + if cluster.Name == clusterName { + if cluster.Status.UUID == "" { + return "", fmt.Errorf("StorageCluster %q found in namespace %q but UUID not yet populated", clusterName, namespace) + } return cluster.Status.UUID, nil } } - return "", fmt.Errorf("cluster %q not found or UUID not ready", clusterName) + return "", fmt.Errorf("StorageCluster %q not found in namespace %q", clusterName, namespace) } func ResolveClusterIdentifier(ctx context.Context, k8sClient client.Client, namespace, cluster string) (string, error) {