diff --git a/controllers/kubernetes.go b/controllers/kubernetes.go index f38f244..415d94f 100644 --- a/controllers/kubernetes.go +++ b/controllers/kubernetes.go @@ -25,7 +25,7 @@ import ( ) func (r *RedkeyClusterReconciler) checkAndCreateK8sObjects(ctx context.Context, req ctrl.Request, redkeyCluster *redkeyv1.RedkeyCluster) (bool, error) { - var immediateRequeue bool = false + var immediateRequeue, immediateRequeueT bool = false, false var err error = nil var configMap *corev1.ConfigMap @@ -37,23 +37,27 @@ func (r *RedkeyClusterReconciler) checkAndCreateK8sObjects(ctx context.Context, } // ConfigMap check - if configMap, immediateRequeue, err = r.checkAndCreateConfigMap(ctx, req, redkeyCluster); err != nil { - return immediateRequeue, err + if configMap, immediateRequeueT, err = r.checkAndCreateConfigMap(ctx, req, redkeyCluster); err != nil { + return immediateRequeueT, err } + immediateRequeue = immediateRequeue || immediateRequeueT // PodDisruptionBudget check r.checkAndManagePodDisruptionBudget(ctx, req, redkeyCluster) // StatefulSet check - if immediateRequeue, err = r.checkAndCreateStatefulSet(ctx, req, redkeyCluster, configMap); err != nil { - return immediateRequeue, err + if immediateRequeueT, err = r.checkAndCreateStatefulSet(ctx, req, redkeyCluster, configMap); err != nil { + return immediateRequeueT, err } + immediateRequeue = immediateRequeue || immediateRequeueT // Robin deployment check - r.checkAndCreateRobin(ctx, req, redkeyCluster) + immediateRequeueT, _ = r.checkAndCreateRobin(ctx, req, redkeyCluster) + immediateRequeue = immediateRequeue || immediateRequeueT // Service check - immediateRequeue, err = r.checkAndCreateService(ctx, req, redkeyCluster) + immediateRequeueT, err = r.checkAndCreateService(ctx, req, redkeyCluster) + immediateRequeue = immediateRequeue || immediateRequeueT return immediateRequeue, err } diff --git a/controllers/robin.go b/controllers/robin.go index 1e3b39e..f78e8ad 100644 --- a/controllers/robin.go +++ b/controllers/robin.go @@ -26,7 +26,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func (r *RedkeyClusterReconciler) checkAndCreateRobin(ctx context.Context, req ctrl.Request, redkeyCluster *redkeyv1.RedkeyCluster) error { +func (r *RedkeyClusterReconciler) checkAndCreateRobin(ctx context.Context, req ctrl.Request, redkeyCluster *redkeyv1.RedkeyCluster) (bool, error) { // Populate robin spec if not provided. This to handle the case where the user removes the robin spec of an existing cluster. The robin objects will be deleted. if redkeyCluster.Spec.Robin == nil { redkeyCluster.Spec.Robin = &redkeyv1.RobinSpec{ @@ -37,13 +37,13 @@ func (r *RedkeyClusterReconciler) checkAndCreateRobin(ctx context.Context, req c // Robin configmap if err := r.handleRobinConfig(ctx, req, redkeyCluster); err != nil { - return err + return false, err } // Robin deployment - r.handleRobinDeployment(ctx, req, redkeyCluster) + immediateRequeue := r.handleRobinDeployment(ctx, req, redkeyCluster) - return nil + return immediateRequeue, nil } func (r *RedkeyClusterReconciler) handleRobinConfig(ctx context.Context, req ctrl.Request, redkeyCluster *redkeyv1.RedkeyCluster) error { @@ -106,14 +106,14 @@ func (r *RedkeyClusterReconciler) handleRobinConfig(ctx context.Context, req ctr return nil } -func (r *RedkeyClusterReconciler) handleRobinDeployment(ctx context.Context, req ctrl.Request, redkeyCluster *redkeyv1.RedkeyCluster) { +func (r *RedkeyClusterReconciler) handleRobinDeployment(ctx context.Context, req ctrl.Request, redkeyCluster *redkeyv1.RedkeyCluster) (immediateRequeue bool) { // Get robin deployment deployment, err := r.FindExistingDeployment(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Name: redkeyCluster.Name + "-robin", Namespace: redkeyCluster.Namespace}}) // Robin deployment template not provided: delete deployment if exists if redkeyCluster.Spec.Robin.Template == nil { r.deleteRobinObject(ctx, deployment, redkeyCluster, "deployment") - return + return false } // Robin deployment not found: create deployment @@ -121,13 +121,13 @@ func (r *RedkeyClusterReconciler) handleRobinDeployment(ctx context.Context, req // Return if the error is not a NotFound error if !errors.IsNotFound(err) { r.logError(redkeyCluster.NamespacedName(), err, "Getting robin deployment failed") - return + return false } // Create Robin Deployment robinDeployment := r.createRobinDeployment(req, redkeyCluster, redkeyCluster.GetLabels()) r.createRobinObject(ctx, robinDeployment, redkeyCluster, "deployment") - return + return false } // Robin deployment found: check if it needs to be updated @@ -149,12 +149,14 @@ func (r *RedkeyClusterReconciler) handleRobinDeployment(ctx context.Context, req } if !changed { - return + return needsScaleUp } // Robin deployment changed: update deployment deployment.Spec.Template = patchedPodTemplateSpec r.updateRobinObject(ctx, deployment, redkeyCluster, "deployment") + + return needsScaleUp } func (r *RedkeyClusterReconciler) createRobinObject(ctx context.Context, obj client.Object, redkeyCluster *redkeyv1.RedkeyCluster, kind string) error { @@ -231,6 +233,11 @@ func (r *RedkeyClusterReconciler) overrideRobinDeployment(req ctrl.Request, redk func (r *RedkeyClusterReconciler) createRobinDeployment(req ctrl.Request, redkeyCluster *redkeyv1.RedkeyCluster, labels map[string]string) *v1.Deployment { var replicas = int32(1) + // Create deployment with 0 replicas if the cluster has 0 primaries + // to avoid pod errors due to missing redis nodes. + if redkeyCluster.Spec.Primaries == 0 { + replicas = 0 + } d := &v1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: req.Name + "-robin", diff --git a/test/e2e/redkey_suite_test.go b/test/e2e/redkey_suite_test.go index ee424ae..9b233da 100644 --- a/test/e2e/redkey_suite_test.go +++ b/test/e2e/redkey_suite_test.go @@ -110,10 +110,11 @@ var _ = Describe("Redkey Operator & RedkeyCluster E2E", Label("operator", "clust Expect(rc.Spec.Primaries).To(Equal(target)) }, - Entry("up → 3", SpecTimeout(simpleTestDuration), int32(0), int32(3)), - Entry("up → 5", SpecTimeout(simpleTestDuration), int32(3), int32(5)), - Entry("down → 3", SpecTimeout(simpleTestDuration), int32(5), int32(3)), - Entry("down → 0", SpecTimeout(simpleTestDuration), int32(3), int32(0)), + Entry("scale up 0 → 3", SpecTimeout(simpleTestDuration), int32(0), int32(3)), + Entry("scale up 3 → 5", SpecTimeout(simpleTestDuration), int32(3), int32(5)), + Entry("scale down 5 → 3", SpecTimeout(simpleTestDuration), int32(5), int32(3)), + Entry("scale down 3 → 0", SpecTimeout(simpleTestDuration), int32(3), int32(0)), + Entry("scale up 0 → 3", SpecTimeout(simpleTestDuration), int32(0), int32(3)), ) })