diff --git a/apis/reaper/v1alpha1/reaper_types.go b/apis/reaper/v1alpha1/reaper_types.go index 9b3cc3d2d..c9bbb820f 100644 --- a/apis/reaper/v1alpha1/reaper_types.go +++ b/apis/reaper/v1alpha1/reaper_types.go @@ -29,6 +29,8 @@ import ( // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. +var SingleReplica = int32(1) + const ( ReaperLabel = "k8ssandra.io/reaper" DefaultKeyspace = "reaper_db" diff --git a/controllers/reaper/reaper_controller.go b/controllers/reaper/reaper_controller.go index 5f561f987..e819482d9 100644 --- a/controllers/reaper/reaper_controller.go +++ b/controllers/reaper/reaper_controller.go @@ -33,6 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" + "math" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -244,6 +245,20 @@ func (r *ReaperReconciler) reconcileDeployment( return ctrl.Result{}, nil } + // if using memory storage, we need to ensure only one Reaper exists ~ the STS has at most 1 replica + if actualReaper.Spec.StorageType == reaperapi.StorageTypeMemory { + desiredReplicas := getDeploymentReplicas(desiredDeployment, logger) + if desiredReplicas > 1 { + logger.Info(fmt.Sprintf("reaper with memory storage can only have one replica, not allowing the %d that are desired", desiredReplicas)) + setDeploymentReplicas(&desiredDeployment, &reaperapi.SingleReplica, logger) + } + actualReplicas := getDeploymentReplicas(actualDeployment, logger) + if actualReplicas > 1 { + logger.Info(fmt.Sprintf("reaper with memory storage currently has %d replicas, scaling down to 1", actualReplicas)) + setDeploymentReplicas(&desiredDeployment, &reaperapi.SingleReplica, logger) + } + } + // Check if the deployment needs to be updated if !annotations.CompareHashAnnotations(actualDeployment, desiredDeployment) { logger.Info("Updating Reaper Deployment") @@ -276,6 +291,29 @@ func (r *ReaperReconciler) reconcileDeployment( return ctrl.Result{}, nil } +func getDeploymentReplicas(actualDeployment client.Object, logger logr.Logger) int32 { + switch actual := actualDeployment.(type) { + case *appsv1.Deployment: + return *actual.Spec.Replicas + case *appsv1.StatefulSet: + return *actual.Spec.Replicas + default: + logger.Error(fmt.Errorf("unexpected type %T", actualDeployment), "Failed to get deployment replicas") + return math.MaxInt32 + } +} + +func setDeploymentReplicas(desiredDeployment *client.Object, singleReplica *int32, logger logr.Logger) { + switch desired := (*desiredDeployment).(type) { + case *appsv1.Deployment: + desired.Spec.Replicas = singleReplica + case *appsv1.StatefulSet: + desired.Spec.Replicas = singleReplica + default: + logger.Error(fmt.Errorf("unexpected type %T", desiredDeployment), "Failed to set deployment replicas") + } +} + func (r *ReaperReconciler) reconcileService( ctx context.Context, actualReaper *reaperapi.Reaper, diff --git a/pkg/reaper/deployment.go b/pkg/reaper/deployment.go index 5080cb385..843c8305b 100644 --- a/pkg/reaper/deployment.go +++ b/pkg/reaper/deployment.go @@ -326,7 +326,7 @@ func computeVolumeClaims(reaper *api.Reaper) []corev1.PersistentVolumeClaim { var volumeClaimsPec *corev1.PersistentVolumeClaimSpec storageClass := api.DefaultStorageClass - accessModes := []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce} + accessModes := []corev1.PersistentVolumeAccessMode{api.DefaultAccessMode} if reaper.Spec.StorageConfig != nil { volumeClaimsPec = reaper.Spec.StorageConfig.DeepCopy() if volumeClaimsPec.Resources.Requests.Storage() == nil { @@ -373,6 +373,7 @@ func NewStatefulSet(reaper *api.Reaper, dc *cassdcapi.CassandraDatacenter, logge Spec: computePodSpec(reaper, dc, nil, nil, nil), }, VolumeClaimTemplates: computeVolumeClaims(reaper), + Replicas: &api.SingleReplica, }, } addAuthEnvVars(&statefulSet.Spec.Template, authVars)