Repository: kubernetes/kubernetes
File: pkg/registry/autoscaling/horizontalpodautoscaler/strategy.go (Go)

Code before:
func (autoscalerStrategy) Canonicalize(obj runtime.Object) {
}

// AllowCreateOnUpdate is false for autoscalers.
func (autoscalerStrategy) AllowCreateOnUpdate() bool {
	return false
}

// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (autoscalerStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
	newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
	oldHPA := old.(*autoscaling.HorizontalPodAutoscaler)
	// Update is not allowed to set status
	newHPA.Status = oldHPA.Status

	dropDisabledFields(newHPA, oldHPA)
}

// ValidateUpdate is the default update validation for an end user.
func (autoscalerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
	newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
	oldHPA := old.(*autoscaling.HorizontalPodAutoscaler)
	opts := validationOptionsForHorizontalPodAutoscaler(newHPA, oldHPA)
	errs := validation.ValidateHorizontalPodAutoscalerUpdate(newHPA, oldHPA, opts)
	var options []string
	if utilfeature.DefaultFeatureGate.Enabled(features.HPAScaleToZero) {
		options = append(options, "HPAScaleToZero")
	}
	return rest.ValidateDeclarativelyWithMigrationChecks(ctx, legacyscheme.Scheme, newHPA, oldHPA, errs, operation.Update, rest.WithOptions(options))
}

// WarningsOnUpdate returns warnings for the given update.
func (autoscalerStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
	return nil
}

func (autoscalerStrategy) AllowUnconditionalUpdate() bool {
	return true
}

type autoscalerStatusStrategy struct {
	autoscalerStrategy
}

// StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = autoscalerStatusStrategy{Strategy}

// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (autoscalerStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
	fields := map[fieldpath.APIVersion]*fieldpath.Set{

Review comment (performance):
This is not right, I think.  Validation `ifEnabled` is not the same as the gate being enabled.  It has to also consider previous state.

The original code:

```
163 func validationOptionsForHorizontalPodAutoscaler(newHPA, oldHPA *autoscaling.HorizontalPodAutoscaler) validation.HorizontalPodAutoscalerSpecValidationOptions {
164     opts := validation.HorizontalPodAutoscalerSpecValidationOptions{
165         MinReplicasLowerBound:           1,
166         ScaleTargetRefValidationOptions: validation.CrossVersionObjectReferenceValidationOptions{AllowInvalidAPIVersion: false, AllowEmptyAPIGroup: false},
167         ObjectMetricsValidationOptions: validation.CrossVersionObjectReferenceValidationOptions{
168             AllowInvalidAPIVersion: false, AllowEmptyAPIGroup: true,
169         },
170     }
171 
172     oldHasZeroMinReplicas := oldHPA != nil && (oldHPA.Spec.MinReplicas != nil && *oldHPA.Spec.MinReplicas == 0)
173     if utilfeature.DefaultFeatureGate.Enabled(features.HPAScaleToZero) || oldHasZeroMinReplicas {                                                                         
174         opts.MinReplicasLowerBound = 0
175     }
```

Code after:
func (autoscalerStrategy) Canonicalize(obj runtime.Object) {
}

// AllowCreateOnUpdate is false for autoscalers.
func (autoscalerStrategy) AllowCreateOnUpdate() bool {
	return false
}

// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (autoscalerStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
	newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
	oldHPA := old.(*autoscaling.HorizontalPodAutoscaler)
	// Update is not allowed to set status
	newHPA.Status = oldHPA.Status

	dropDisabledFields(newHPA, oldHPA)
}

// ValidateUpdate is the default update validation for an end user.
func (autoscalerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
	newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
	oldHPA := old.(*autoscaling.HorizontalPodAutoscaler)
	opts := validationOptionsForHorizontalPodAutoscaler(newHPA, oldHPA)
	errs := validation.ValidateHorizontalPodAutoscalerUpdate(newHPA, oldHPA, opts)
	var options []string
	oldHasZeroMinReplicas := oldHPA.Spec.MinReplicas != nil && *oldHPA.Spec.MinReplicas == 0
	if utilfeature.DefaultFeatureGate.Enabled(features.HPAScaleToZero) || oldHasZeroMinReplicas {
		options = append(options, "HPAScaleToZero")
	}
	return rest.ValidateDeclarativelyWithMigrationChecks(ctx, legacyscheme.Scheme, newHPA, oldHPA, errs, operation.Update, rest.WithOptions(options))
}

// WarningsOnUpdate returns warnings for the given update.
func (autoscalerStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
	return nil
}

func (autoscalerStrategy) AllowUnconditionalUpdate() bool {
	return true
}

type autoscalerStatusStrategy struct {
	autoscalerStrategy
}

// StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = autoscalerStatusStrategy{Strategy}

// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (autoscalerStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {