...

Source file src/k8s.io/kubernetes/pkg/registry/policy/poddisruptionbudget/strategy.go

Documentation: k8s.io/kubernetes/pkg/registry/policy/poddisruptionbudget

     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 poddisruptionbudget
    18  
    19  import (
    20  	"context"
    21  
    22  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    23  	metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/apimachinery/pkg/runtime/schema"
    26  	"k8s.io/apimachinery/pkg/util/validation/field"
    27  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    28  	"k8s.io/apiserver/pkg/storage/names"
    29  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    30  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    31  	"k8s.io/kubernetes/pkg/apis/policy"
    32  	"k8s.io/kubernetes/pkg/apis/policy/validation"
    33  	"k8s.io/kubernetes/pkg/features"
    34  	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
    35  )
    36  
    37  // podDisruptionBudgetStrategy implements verification logic for PodDisruptionBudgets.
    38  type podDisruptionBudgetStrategy struct {
    39  	runtime.ObjectTyper
    40  	names.NameGenerator
    41  }
    42  
    43  // Strategy is the default logic that applies when creating and updating PodDisruptionBudget objects.
    44  var Strategy = podDisruptionBudgetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
    45  
    46  // NamespaceScoped returns true because all PodDisruptionBudget' need to be within a namespace.
    47  func (podDisruptionBudgetStrategy) NamespaceScoped() bool {
    48  	return true
    49  }
    50  
    51  // GetResetFields returns the set of fields that get reset by the strategy
    52  // and should not be modified by the user.
    53  func (podDisruptionBudgetStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
    54  	fields := map[fieldpath.APIVersion]*fieldpath.Set{
    55  		"policy/v1beta1": fieldpath.NewSet(
    56  			fieldpath.MakePathOrDie("status"),
    57  		),
    58  		"policy/v1": fieldpath.NewSet(
    59  			fieldpath.MakePathOrDie("status"),
    60  		),
    61  	}
    62  
    63  	return fields
    64  }
    65  
    66  // PrepareForCreate clears the status of an PodDisruptionBudget before creation.
    67  func (podDisruptionBudgetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
    68  	podDisruptionBudget := obj.(*policy.PodDisruptionBudget)
    69  	// create cannot set status
    70  	podDisruptionBudget.Status = policy.PodDisruptionBudgetStatus{}
    71  
    72  	podDisruptionBudget.Generation = 1
    73  
    74  	dropDisabledFields(&podDisruptionBudget.Spec, nil)
    75  }
    76  
    77  // PrepareForUpdate clears fields that are not allowed to be set by end users on update.
    78  func (podDisruptionBudgetStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
    79  	newPodDisruptionBudget := obj.(*policy.PodDisruptionBudget)
    80  	oldPodDisruptionBudget := old.(*policy.PodDisruptionBudget)
    81  	// Update is not allowed to set status
    82  	newPodDisruptionBudget.Status = oldPodDisruptionBudget.Status
    83  
    84  	// Any changes to the spec increment the generation number, any changes to the
    85  	// status should reflect the generation number of the corresponding object.
    86  	// See metav1.ObjectMeta description for more information on Generation.
    87  	if !apiequality.Semantic.DeepEqual(oldPodDisruptionBudget.Spec, newPodDisruptionBudget.Spec) {
    88  		newPodDisruptionBudget.Generation = oldPodDisruptionBudget.Generation + 1
    89  	}
    90  
    91  	dropDisabledFields(&newPodDisruptionBudget.Spec, &oldPodDisruptionBudget.Spec)
    92  }
    93  
    94  // Validate validates a new PodDisruptionBudget.
    95  func (podDisruptionBudgetStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
    96  	podDisruptionBudget := obj.(*policy.PodDisruptionBudget)
    97  	opts := validation.PodDisruptionBudgetValidationOptions{
    98  		AllowInvalidLabelValueInSelector: false,
    99  	}
   100  	return validation.ValidatePodDisruptionBudget(podDisruptionBudget, opts)
   101  }
   102  
   103  // WarningsOnCreate returns warnings for the creation of the given object.
   104  func (podDisruptionBudgetStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
   105  	return nil
   106  }
   107  
   108  // Canonicalize normalizes the object after validation.
   109  func (podDisruptionBudgetStrategy) Canonicalize(obj runtime.Object) {
   110  }
   111  
   112  // AllowCreateOnUpdate is true for PodDisruptionBudget; this means you may create one with a PUT request.
   113  func (podDisruptionBudgetStrategy) AllowCreateOnUpdate() bool {
   114  	return false
   115  }
   116  
   117  // ValidateUpdate is the default update validation for an end user.
   118  func (podDisruptionBudgetStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
   119  	opts := validation.PodDisruptionBudgetValidationOptions{
   120  		AllowInvalidLabelValueInSelector: hasInvalidLabelValueInLabelSelector(old.(*policy.PodDisruptionBudget)),
   121  	}
   122  	return validation.ValidatePodDisruptionBudget(obj.(*policy.PodDisruptionBudget), opts)
   123  }
   124  
   125  // WarningsOnUpdate returns warnings for the given update.
   126  func (podDisruptionBudgetStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
   127  	return nil
   128  }
   129  
   130  // AllowUnconditionalUpdate is the default update policy for PodDisruptionBudget objects. Status update should
   131  // only be allowed if version match.
   132  func (podDisruptionBudgetStrategy) AllowUnconditionalUpdate() bool {
   133  	return false
   134  }
   135  
   136  type podDisruptionBudgetStatusStrategy struct {
   137  	podDisruptionBudgetStrategy
   138  }
   139  
   140  // StatusStrategy is the default logic invoked when updating object status.
   141  var StatusStrategy = podDisruptionBudgetStatusStrategy{Strategy}
   142  
   143  // GetResetFields returns the set of fields that get reset by the strategy
   144  // and should not be modified by the user.
   145  func (podDisruptionBudgetStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
   146  	fields := map[fieldpath.APIVersion]*fieldpath.Set{
   147  		"policy/v1beta1": fieldpath.NewSet(
   148  			fieldpath.MakePathOrDie("spec"),
   149  		),
   150  		"policy/v1": fieldpath.NewSet(
   151  			fieldpath.MakePathOrDie("spec"),
   152  		),
   153  	}
   154  
   155  	return fields
   156  }
   157  
   158  // PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
   159  func (podDisruptionBudgetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
   160  	newPodDisruptionBudget := obj.(*policy.PodDisruptionBudget)
   161  	oldPodDisruptionBudget := old.(*policy.PodDisruptionBudget)
   162  	// status changes are not allowed to update spec
   163  	newPodDisruptionBudget.Spec = oldPodDisruptionBudget.Spec
   164  }
   165  
   166  // ValidateUpdate is the default update validation for an end user updating status
   167  func (podDisruptionBudgetStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
   168  	var apiVersion schema.GroupVersion
   169  	if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
   170  		apiVersion = schema.GroupVersion{
   171  			Group:   requestInfo.APIGroup,
   172  			Version: requestInfo.APIVersion,
   173  		}
   174  	}
   175  	return validation.ValidatePodDisruptionBudgetStatusUpdate(obj.(*policy.PodDisruptionBudget).Status,
   176  		old.(*policy.PodDisruptionBudget).Status, field.NewPath("status"), apiVersion)
   177  }
   178  
   179  // WarningsOnUpdate returns warnings for the given update.
   180  func (podDisruptionBudgetStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
   181  	return nil
   182  }
   183  
   184  func hasInvalidLabelValueInLabelSelector(pdb *policy.PodDisruptionBudget) bool {
   185  	if pdb.Spec.Selector != nil {
   186  		labelSelectorValidationOptions := metav1validation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}
   187  		return len(metav1validation.ValidateLabelSelector(pdb.Spec.Selector, labelSelectorValidationOptions, nil)) > 0
   188  	}
   189  	return false
   190  }
   191  
   192  // dropDisabledFields removes disabled fields from the pod disruption budget spec.
   193  // This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pod disruption budget spec.
   194  func dropDisabledFields(pdbSpec, oldPDBSpec *policy.PodDisruptionBudgetSpec) {
   195  	if !utilfeature.DefaultFeatureGate.Enabled(features.PDBUnhealthyPodEvictionPolicy) {
   196  		if !unhealthyPodEvictionPolicyInUse(oldPDBSpec) {
   197  			pdbSpec.UnhealthyPodEvictionPolicy = nil
   198  		}
   199  	}
   200  }
   201  
   202  func unhealthyPodEvictionPolicyInUse(oldPDBSpec *policy.PodDisruptionBudgetSpec) bool {
   203  	if oldPDBSpec == nil {
   204  		return false
   205  	}
   206  	if oldPDBSpec.UnhealthyPodEvictionPolicy != nil {
   207  		return true
   208  	}
   209  	return false
   210  }
   211  

View as plain text