Enable declarative validation for replicationcontroller/scale

This commit is contained in:
Joe Betz
2025-03-19 22:54:29 -04:00
parent 030b4ab9cc
commit 356f395f0d
2 changed files with 33 additions and 6 deletions

View File

@@ -32,12 +32,15 @@ import (
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/autoscaling"
autoscalingv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
"k8s.io/kubernetes/pkg/apis/autoscaling/validation"
api "k8s.io/kubernetes/pkg/apis/core"
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
@@ -200,7 +203,7 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
obj, _, err := r.store.Update(
ctx,
name,
&scaleUpdatedObjectInfo{name, objInfo},
&scaleUpdatedObjectInfo{name, objInfo, r},
toScaleCreateValidation(createValidation),
toScaleUpdateValidation(updateValidation),
false,
@@ -255,8 +258,9 @@ func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale {
// scaleUpdatedObjectInfo transforms existing replication controller -> existing scale -> new scale -> new replication controller
type scaleUpdatedObjectInfo struct {
name string
reqObjInfo rest.UpdatedObjectInfo
name string
reqObjInfo rest.UpdatedObjectInfo
scaleGVKMapper rest.GroupVersionKindProvider
}
func (i *scaleUpdatedObjectInfo) Preconditions() *metav1.Preconditions {
@@ -310,8 +314,27 @@ func (i *scaleUpdatedObjectInfo) UpdatedObject(ctx context.Context, oldObj runti
return nil, errors.NewBadRequest(fmt.Sprintf("expected input object type to be Scale, but %T", newScaleObj))
}
// validate
if errs := validation.ValidateScale(scale); len(errs) > 0 {
errs := validation.ValidateScale(scale)
// If DeclarativeValidation feature gate is enabled, also run declarative validation
if utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidation) {
// Determine if takeover is enabled
takeover := utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidationTakeover)
// Run declarative validation with panic recovery
declarativeErrs := rest.ValidateUpdateDeclaratively(
ctx, legacyscheme.Scheme, scale, oldScale, rest.WithTakeover(takeover), rest.WithSubresourceMapper(i.scaleGVKMapper))
// Compare imperative and declarative errors and log + emit metric if there's a mismatch
rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, errs, declarativeErrs, takeover)
// Only apply declarative errors if takeover is enabled
if takeover {
errs = append(errs.RemoveCoveredByDeclarative(), declarativeErrs...)
}
}
if len(errs) > 0 {
return nil, errors.NewInvalid(autoscaling.Kind("Scale"), replicationcontroller.Name, errs)
}

View File

@@ -23,6 +23,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -63,7 +64,10 @@ func newStorage(t *testing.T) (ControllerStorage, *etcd3testing.EtcdTestServer)
// createController is a helper function that returns a controller with the updated resource version.
func createController(storage *REST, rc api.ReplicationController, t *testing.T) (api.ReplicationController, error) {
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), rc.Namespace)
ctx := genericapirequest.WithRequestInfo(
genericapirequest.WithNamespace(genericapirequest.NewContext(), rc.Namespace),
&genericapirequest.RequestInfo{APIGroup: "", APIVersion: "v1"},
)
obj, err := storage.Create(ctx, &rc, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
if err != nil {
t.Errorf("Failed to create controller, %v", err)