diff --git a/pkg/controller/main-controller.go b/pkg/controller/main-controller.go index 40549a4b938..6e0b17b744e 100644 --- a/pkg/controller/main-controller.go +++ b/pkg/controller/main-controller.go @@ -43,8 +43,6 @@ import ( "k8s.io/client-go/tools/leaderelection" "k8s.io/client-go/tools/leaderelection/resourcelock" - miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1" - "golang.org/x/time/rate" // Workaround for auth import issues refer https://github.com/minio/operator/issues/283 @@ -1202,8 +1200,15 @@ func (c *Controller) syncHandler(key string) (Result, error) { } for i, pool := range tenant.Spec.Pools { + ssName := tenant.PoolStatefulsetName(&pool) + existingStatefulSet, err := c.statefulSetLister.StatefulSets(tenant.Namespace).Get(ssName) + if k8serrors.IsNotFound(err) { + klog.Errorf("%s's pool %s doesn't exist: %v", tenant.Name, ssName, err) + return WrapResult(Result{}, err) + } + // Now proceed to make the yaml changes for the tenant statefulset. - ss := statefulsets.NewPool(&statefulsets.NewPoolArgs{ + expectedStatefulSet := statefulsets.NewPool(&statefulsets.NewPoolArgs{ Tenant: tenant, SkipEnvVars: skipEnvVars, Pool: &pool, @@ -1212,7 +1217,9 @@ func (c *Controller) syncHandler(key string) (Result, error) { HostsTemplate: c.hostsTemplate, OperatorVersion: c.operatorVersion, }) - if _, err = c.kubeClientSet.AppsV1().StatefulSets(tenant.Namespace).Update(ctx, ss, uOpts); err != nil { + + newStatefulSet := statefulsets.UpdateStatefulSetSpec(existingStatefulSet, expectedStatefulSet) + if _, err = c.kubeClientSet.AppsV1().StatefulSets(tenant.Namespace).Update(ctx, newStatefulSet, uOpts); err != nil { return WrapResult(Result{}, err) } c.recorder.Event(tenant, corev1.EventTypeNormal, "PoolUpdated", fmt.Sprintf("Tenant pool %s updated", pool.Name)) @@ -1274,24 +1281,7 @@ func (c *Controller) syncHandler(key string) (Result, error) { } // if the pool doesn't match the spec if !poolMatchesSS { - // for legacy reasons, if the zone label is present in SS we must carry it over - carryOverLabels := make(map[string]string) - if val, ok := existingStatefulSet.Spec.Template.ObjectMeta.Labels[miniov1.ZoneLabel]; ok { - carryOverLabels[miniov1.ZoneLabel] = val - } - - newStatefulSet := existingStatefulSet.DeepCopy() - - newStatefulSet.Spec.Template = expectedStatefulSet.Spec.Template - newStatefulSet.Spec.UpdateStrategy = expectedStatefulSet.Spec.UpdateStrategy - - if existingStatefulSet.Spec.Template.ObjectMeta.Labels == nil { - newStatefulSet.Spec.Template.ObjectMeta.Labels = make(map[string]string) - } - for k, v := range carryOverLabels { - newStatefulSet.Spec.Template.ObjectMeta.Labels[k] = v - } - + newStatefulSet := statefulsets.UpdateStatefulSetSpec(existingStatefulSet, expectedStatefulSet) if existingStatefulSet, err = c.kubeClientSet.AppsV1().StatefulSets(tenant.Namespace).Update(ctx, newStatefulSet, uOpts); err != nil { klog.Errorf("[Will try again in 5sec] Update tenant %s statefulset %s error %s", tenant.Name, ssName, err) return WrapResult(Result{RequeueAfter: time.Second * 5}, nil) diff --git a/pkg/resources/statefulsets/minio-statefulset.go b/pkg/resources/statefulsets/minio-statefulset.go index 71de5f959d4..e89f7191566 100644 --- a/pkg/resources/statefulsets/minio-statefulset.go +++ b/pkg/resources/statefulsets/minio-statefulset.go @@ -21,6 +21,7 @@ import ( "strconv" "strings" + miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1" "k8s.io/apimachinery/pkg/util/intstr" "github.com/minio/operator/pkg/certs" @@ -947,3 +948,35 @@ func getSideCarContainer(t *miniov2.Tenant, pool *miniov2.Pool) corev1.Container } return sidecarContainer } + +// UpdateStatefulSetSpec updates the spec of an existing StatefulSet with the spec of the expected StatefulSet. +// It only updates the fields that are allowed to be updated in Kubernetes StatefulSets, which include `replicas`, +// `ordinals`, `template`, `updateStrategy`, `persistentVolumeClaimRetentionPolicy`, and `minReadySeconds`. Additionally, +// it ensures that specific legacy labels, such as the zone label, are carried over to the new spec. +// +// Parameters: +// - existingStatefulSet: The current StatefulSet object. +// - expectedStatefulSet: The desired StatefulSet object with the updated spec. +// +// Returns: +// - A new StatefulSet object with the updated spec and preserved legacy labels. +func UpdateStatefulSetSpec(existingStatefulSet *appsv1.StatefulSet, expectedStatefulSet *appsv1.StatefulSet) *appsv1.StatefulSet { + // for legacy reasons, if the zone label is present in SS we must carry it over + carryOverLabels := make(map[string]string) + if val, ok := existingStatefulSet.Spec.Template.ObjectMeta.Labels[miniov1.ZoneLabel]; ok { + carryOverLabels[miniov1.ZoneLabel] = val + } + + newStatefulSet := existingStatefulSet.DeepCopy() + + newStatefulSet.Spec.Template = expectedStatefulSet.Spec.Template + newStatefulSet.Spec.UpdateStrategy = expectedStatefulSet.Spec.UpdateStrategy + + if existingStatefulSet.Spec.Template.ObjectMeta.Labels == nil { + newStatefulSet.Spec.Template.ObjectMeta.Labels = make(map[string]string) + } + for k, v := range carryOverLabels { + newStatefulSet.Spec.Template.ObjectMeta.Labels[k] = v + } + return newStatefulSet +}