...

Source file src/k8s.io/kubernetes/pkg/registry/apps/statefulset/strategy.go

Documentation: k8s.io/kubernetes/pkg/registry/apps/statefulset

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package statefulset
    18  
    19  import (
    20  	"context"
    21  
    22  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    23  	"k8s.io/apimachinery/pkg/runtime"
    24  	"k8s.io/apimachinery/pkg/util/validation/field"
    25  	"k8s.io/apiserver/pkg/registry/rest"
    26  	"k8s.io/apiserver/pkg/storage/names"
    27  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    28  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    29  	pvcutil "k8s.io/kubernetes/pkg/api/persistentvolumeclaim"
    30  	"k8s.io/kubernetes/pkg/api/pod"
    31  	"k8s.io/kubernetes/pkg/apis/apps"
    32  	"k8s.io/kubernetes/pkg/apis/apps/validation"
    33  	"k8s.io/kubernetes/pkg/features"
    34  	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
    35  )
    36  
    37  // statefulSetStrategy implements verification logic for Replication StatefulSets.
    38  type statefulSetStrategy struct {
    39  	runtime.ObjectTyper
    40  	names.NameGenerator
    41  }
    42  
    43  // Strategy is the default logic that applies when creating and updating Replication StatefulSet objects.
    44  var Strategy = statefulSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
    45  
    46  // Make sure we correctly implement the interface.
    47  var _ = rest.GarbageCollectionDeleteStrategy(Strategy)
    48  
    49  // DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions.
    50  func (statefulSetStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
    51  	return rest.DeleteDependents
    52  }
    53  
    54  // NamespaceScoped returns true because all StatefulSet' need to be within a namespace.
    55  func (statefulSetStrategy) NamespaceScoped() bool {
    56  	return true
    57  }
    58  
    59  // GetResetFields returns the set of fields that get reset by the strategy
    60  // and should not be modified by the user.
    61  func (statefulSetStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
    62  	fields := map[fieldpath.APIVersion]*fieldpath.Set{
    63  		"apps/v1": fieldpath.NewSet(
    64  			fieldpath.MakePathOrDie("status"),
    65  		),
    66  	}
    67  
    68  	return fields
    69  }
    70  
    71  // PrepareForCreate clears the status of an StatefulSet before creation.
    72  func (statefulSetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
    73  	statefulSet := obj.(*apps.StatefulSet)
    74  	// create cannot set status
    75  	statefulSet.Status = apps.StatefulSetStatus{}
    76  
    77  	statefulSet.Generation = 1
    78  
    79  	dropStatefulSetDisabledFields(statefulSet, nil)
    80  	pod.DropDisabledTemplateFields(&statefulSet.Spec.Template, nil)
    81  }
    82  
    83  // maxUnavailableInUse returns true if StatefulSet's maxUnavailable set(used)
    84  func maxUnavailableInUse(statefulset *apps.StatefulSet) bool {
    85  	if statefulset == nil {
    86  		return false
    87  	}
    88  	if statefulset.Spec.UpdateStrategy.RollingUpdate == nil {
    89  		return false
    90  	}
    91  
    92  	return statefulset.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable != nil
    93  }
    94  
    95  // PrepareForUpdate clears fields that are not allowed to be set by end users on update.
    96  func (statefulSetStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
    97  	newStatefulSet := obj.(*apps.StatefulSet)
    98  	oldStatefulSet := old.(*apps.StatefulSet)
    99  	// Update is not allowed to set status
   100  	newStatefulSet.Status = oldStatefulSet.Status
   101  
   102  	dropStatefulSetDisabledFields(newStatefulSet, oldStatefulSet)
   103  	pod.DropDisabledTemplateFields(&newStatefulSet.Spec.Template, &oldStatefulSet.Spec.Template)
   104  
   105  	// Any changes to the spec increment the generation number, any changes to the
   106  	// status should reflect the generation number of the corresponding object.
   107  	// See metav1.ObjectMeta description for more information on Generation.
   108  	if !apiequality.Semantic.DeepEqual(oldStatefulSet.Spec, newStatefulSet.Spec) {
   109  		newStatefulSet.Generation = oldStatefulSet.Generation + 1
   110  	}
   111  }
   112  
   113  // dropStatefulSetDisabledFields drops fields that are not used if their associated feature gates
   114  // are not enabled.
   115  // The typical pattern is:
   116  //
   117  //	if !utilfeature.DefaultFeatureGate.Enabled(features.MyFeature) && !myFeatureInUse(oldSvc) {
   118  //	    newSvc.Spec.MyFeature = nil
   119  //	}
   120  func dropStatefulSetDisabledFields(newSS *apps.StatefulSet, oldSS *apps.StatefulSet) {
   121  	if !utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) {
   122  		if oldSS == nil || oldSS.Spec.PersistentVolumeClaimRetentionPolicy == nil {
   123  			newSS.Spec.PersistentVolumeClaimRetentionPolicy = nil
   124  		}
   125  	}
   126  	if !utilfeature.DefaultFeatureGate.Enabled(features.MaxUnavailableStatefulSet) && !maxUnavailableInUse(oldSS) {
   127  		if newSS.Spec.UpdateStrategy.RollingUpdate != nil {
   128  			newSS.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable = nil
   129  		}
   130  	}
   131  	if !utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetStartOrdinal) {
   132  		if oldSS == nil || oldSS.Spec.Ordinals == nil {
   133  			// Reset Spec.Ordinals to the default value (nil).
   134  			newSS.Spec.Ordinals = nil
   135  		}
   136  	}
   137  }
   138  
   139  // Validate validates a new StatefulSet.
   140  func (statefulSetStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
   141  	statefulSet := obj.(*apps.StatefulSet)
   142  	opts := pod.GetValidationOptionsFromPodTemplate(&statefulSet.Spec.Template, nil)
   143  	return validation.ValidateStatefulSet(statefulSet, opts)
   144  }
   145  
   146  // WarningsOnCreate returns warnings for the creation of the given object.
   147  func (statefulSetStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
   148  	newStatefulSet := obj.(*apps.StatefulSet)
   149  	warnings := pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newStatefulSet.Spec.Template, nil)
   150  	for i, pvc := range newStatefulSet.Spec.VolumeClaimTemplates {
   151  		warnings = append(warnings, pvcutil.GetWarningsForPersistentVolumeClaimSpec(field.NewPath("spec", "volumeClaimTemplates").Index(i), pvc.Spec)...)
   152  	}
   153  	return warnings
   154  }
   155  
   156  // Canonicalize normalizes the object after validation.
   157  func (statefulSetStrategy) Canonicalize(obj runtime.Object) {
   158  }
   159  
   160  // AllowCreateOnUpdate is false for StatefulSet; this means POST is needed to create one.
   161  func (statefulSetStrategy) AllowCreateOnUpdate() bool {
   162  	return false
   163  }
   164  
   165  // ValidateUpdate is the default update validation for an end user.
   166  func (statefulSetStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
   167  	newStatefulSet := obj.(*apps.StatefulSet)
   168  	oldStatefulSet := old.(*apps.StatefulSet)
   169  
   170  	opts := pod.GetValidationOptionsFromPodTemplate(&newStatefulSet.Spec.Template, &oldStatefulSet.Spec.Template)
   171  	return validation.ValidateStatefulSetUpdate(newStatefulSet, oldStatefulSet, opts)
   172  }
   173  
   174  // WarningsOnUpdate returns warnings for the given update.
   175  func (statefulSetStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
   176  	var warnings []string
   177  	newStatefulSet := obj.(*apps.StatefulSet)
   178  	oldStatefulSet := old.(*apps.StatefulSet)
   179  	if newStatefulSet.Generation != oldStatefulSet.Generation {
   180  		warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newStatefulSet.Spec.Template, &oldStatefulSet.Spec.Template)
   181  	}
   182  	for i, pvc := range newStatefulSet.Spec.VolumeClaimTemplates {
   183  		warnings = append(warnings, pvcutil.GetWarningsForPersistentVolumeClaimSpec(field.NewPath("spec", "volumeClaimTemplates").Index(i).Child("Spec"), pvc.Spec)...)
   184  	}
   185  
   186  	return warnings
   187  }
   188  
   189  // AllowUnconditionalUpdate is the default update policy for StatefulSet objects.
   190  func (statefulSetStrategy) AllowUnconditionalUpdate() bool {
   191  	return true
   192  }
   193  
   194  type statefulSetStatusStrategy struct {
   195  	statefulSetStrategy
   196  }
   197  
   198  // StatusStrategy is the default logic invoked when updating object status.
   199  var StatusStrategy = statefulSetStatusStrategy{Strategy}
   200  
   201  // GetResetFields returns the set of fields that get reset by the strategy
   202  // and should not be modified by the user.
   203  func (statefulSetStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
   204  	return map[fieldpath.APIVersion]*fieldpath.Set{
   205  		"apps/v1": fieldpath.NewSet(
   206  			fieldpath.MakePathOrDie("spec"),
   207  		),
   208  	}
   209  }
   210  
   211  // PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
   212  func (statefulSetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
   213  	newStatefulSet := obj.(*apps.StatefulSet)
   214  	oldStatefulSet := old.(*apps.StatefulSet)
   215  	// status changes are not allowed to update spec
   216  	newStatefulSet.Spec = oldStatefulSet.Spec
   217  }
   218  
   219  // ValidateUpdate is the default update validation for an end user updating status
   220  func (statefulSetStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
   221  	// TODO: Validate status updates.
   222  	return validation.ValidateStatefulSetStatusUpdate(obj.(*apps.StatefulSet), old.(*apps.StatefulSet))
   223  }
   224  
   225  // WarningsOnUpdate returns warnings for the given update.
   226  func (statefulSetStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
   227  	return nil
   228  }
   229  

View as plain text