mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 03:11:40 +00:00
Fix scale subresource when used with admission webhooks
This commit is contained in:
parent
3bbd9b2c55
commit
dced88e703
@ -293,7 +293,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
|
||||
deployment.Spec.Replicas = scale.Spec.Replicas
|
||||
deployment.ResourceVersion = scale.ResourceVersion
|
||||
obj, _, err = r.store.Update(ctx, deployment.Name, rest.DefaultUpdatedObjectInfo(deployment), createValidation, updateValidation, false, options)
|
||||
obj, _, err = r.store.Update(
|
||||
ctx,
|
||||
deployment.Name,
|
||||
rest.DefaultUpdatedObjectInfo(deployment),
|
||||
toScaleCreateValidation(createValidation),
|
||||
toScaleUpdateValidation(updateValidation),
|
||||
false,
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -305,6 +313,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
return newScale, false, nil
|
||||
}
|
||||
|
||||
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
|
||||
return func(obj runtime.Object) error {
|
||||
scale, err := scaleFromDeployment(obj.(*apps.Deployment))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(scale)
|
||||
}
|
||||
}
|
||||
|
||||
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
|
||||
return func(obj, old runtime.Object) error {
|
||||
newScale, err := scaleFromDeployment(obj.(*apps.Deployment))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldScale, err := scaleFromDeployment(old.(*apps.Deployment))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(newScale, oldScale)
|
||||
}
|
||||
}
|
||||
|
||||
// scaleFromDeployment returns a scale subresource for a deployment.
|
||||
func scaleFromDeployment(deployment *apps.Deployment) (*autoscaling.Scale, error) {
|
||||
selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
|
||||
|
@ -201,7 +201,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
|
||||
rs.Spec.Replicas = scale.Spec.Replicas
|
||||
rs.ResourceVersion = scale.ResourceVersion
|
||||
obj, _, err = r.store.Update(ctx, rs.Name, rest.DefaultUpdatedObjectInfo(rs), createValidation, updateValidation, false, options)
|
||||
obj, _, err = r.store.Update(
|
||||
ctx,
|
||||
rs.Name,
|
||||
rest.DefaultUpdatedObjectInfo(rs),
|
||||
toScaleCreateValidation(createValidation),
|
||||
toScaleUpdateValidation(updateValidation),
|
||||
false,
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -213,6 +221,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
return newScale, false, err
|
||||
}
|
||||
|
||||
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
|
||||
return func(obj runtime.Object) error {
|
||||
scale, err := scaleFromReplicaSet(obj.(*apps.ReplicaSet))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(scale)
|
||||
}
|
||||
}
|
||||
|
||||
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
|
||||
return func(obj, old runtime.Object) error {
|
||||
newScale, err := scaleFromReplicaSet(obj.(*apps.ReplicaSet))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldScale, err := scaleFromReplicaSet(old.(*apps.ReplicaSet))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(newScale, oldScale)
|
||||
}
|
||||
}
|
||||
|
||||
// scaleFromReplicaSet returns a scale subresource for a replica set.
|
||||
func scaleFromReplicaSet(rs *apps.ReplicaSet) (*autoscaling.Scale, error) {
|
||||
selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)
|
||||
|
@ -188,7 +188,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
|
||||
ss.Spec.Replicas = scale.Spec.Replicas
|
||||
ss.ResourceVersion = scale.ResourceVersion
|
||||
obj, _, err = r.store.Update(ctx, ss.Name, rest.DefaultUpdatedObjectInfo(ss), createValidation, updateValidation, false, options)
|
||||
obj, _, err = r.store.Update(
|
||||
ctx,
|
||||
ss.Name,
|
||||
rest.DefaultUpdatedObjectInfo(ss),
|
||||
toScaleCreateValidation(createValidation),
|
||||
toScaleUpdateValidation(updateValidation),
|
||||
false,
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -200,6 +208,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
return newScale, false, err
|
||||
}
|
||||
|
||||
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
|
||||
return func(obj runtime.Object) error {
|
||||
scale, err := scaleFromStatefulSet(obj.(*apps.StatefulSet))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(scale)
|
||||
}
|
||||
}
|
||||
|
||||
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
|
||||
return func(obj, old runtime.Object) error {
|
||||
newScale, err := scaleFromStatefulSet(obj.(*apps.StatefulSet))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldScale, err := scaleFromStatefulSet(old.(*apps.StatefulSet))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(newScale, oldScale)
|
||||
}
|
||||
}
|
||||
|
||||
// scaleFromStatefulSet returns a scale subresource for a statefulset.
|
||||
func scaleFromStatefulSet(ss *apps.StatefulSet) (*autoscaling.Scale, error) {
|
||||
selector, err := metav1.LabelSelectorAsSelector(ss.Spec.Selector)
|
||||
|
@ -183,7 +183,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
|
||||
rc.Spec.Replicas = scale.Spec.Replicas
|
||||
rc.ResourceVersion = scale.ResourceVersion
|
||||
obj, _, err = r.store.Update(ctx, rc.Name, rest.DefaultUpdatedObjectInfo(rc), createValidation, updateValidation, false, options)
|
||||
obj, _, err = r.store.Update(
|
||||
ctx,
|
||||
rc.Name,
|
||||
rest.DefaultUpdatedObjectInfo(rc),
|
||||
toScaleCreateValidation(createValidation),
|
||||
toScaleUpdateValidation(updateValidation),
|
||||
false,
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -191,6 +199,21 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
return scaleFromRC(rc), false, nil
|
||||
}
|
||||
|
||||
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
|
||||
return func(obj runtime.Object) error {
|
||||
return f(scaleFromRC(obj.(*api.ReplicationController)))
|
||||
}
|
||||
}
|
||||
|
||||
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
|
||||
return func(obj, old runtime.Object) error {
|
||||
return f(
|
||||
scaleFromRC(obj.(*api.ReplicationController)),
|
||||
scaleFromRC(old.(*api.ReplicationController)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// scaleFromRC returns a scale subresource for a replication controller.
|
||||
func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale {
|
||||
return &autoscaling.Scale{
|
||||
|
@ -95,7 +95,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
|
||||
rc.Spec.Replicas = scale.Spec.Replicas
|
||||
rc.ResourceVersion = scale.ResourceVersion
|
||||
obj, _, err = r.store.Update(ctx, rc.Name, rest.DefaultUpdatedObjectInfo(rc), createValidation, updateValidation, false, options)
|
||||
obj, _, err = r.store.Update(
|
||||
ctx,
|
||||
rc.Name,
|
||||
rest.DefaultUpdatedObjectInfo(rc),
|
||||
toScaleCreateValidation(createValidation),
|
||||
toScaleUpdateValidation(updateValidation),
|
||||
false,
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, errors.NewConflict(extensions.Resource("replicationcontrollers/scale"), scale.Name, err)
|
||||
}
|
||||
@ -103,6 +111,21 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
return scaleFromRC(rc), false, nil
|
||||
}
|
||||
|
||||
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
|
||||
return func(obj runtime.Object) error {
|
||||
return f(scaleFromRC(obj.(*api.ReplicationController)))
|
||||
}
|
||||
}
|
||||
|
||||
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
|
||||
return func(obj, old runtime.Object) error {
|
||||
return f(
|
||||
scaleFromRC(obj.(*api.ReplicationController)),
|
||||
scaleFromRC(old.(*api.ReplicationController)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// scaleFromRC returns a scale subresource for a replication controller.
|
||||
func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale {
|
||||
return &autoscaling.Scale{
|
||||
|
@ -12,6 +12,7 @@ go_library(
|
||||
importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver/conversion",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
|
||||
@ -22,6 +23,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||
],
|
||||
|
@ -19,6 +19,7 @@ package conversion
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@ -26,6 +27,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/apiserver/pkg/util/webhook"
|
||||
typedscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
)
|
||||
|
||||
// CRConverterFactory is the factory for all CR converters.
|
||||
@ -77,7 +79,20 @@ func (m *CRConverterFactory) NewConverter(crd *apiextensions.CustomResourceDefin
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unknown conversion strategy %q for CRD %s", crd.Spec.Conversion.Strategy, crd.Name)
|
||||
}
|
||||
|
||||
// Determine whether we should expect to be asked to "convert" autoscaling/v1 Scale types
|
||||
convertScale := false
|
||||
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) {
|
||||
convertScale = crd.Spec.Subresources != nil && crd.Spec.Subresources.Scale != nil
|
||||
for _, version := range crd.Spec.Versions {
|
||||
if version.Subresources != nil && version.Subresources.Scale != nil {
|
||||
convertScale = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe = &crConverter{
|
||||
convertScale: convertScale,
|
||||
validVersions: validVersions,
|
||||
clusterScoped: crd.Spec.Scope == apiextensions.ClusterScoped,
|
||||
converter: converter,
|
||||
@ -96,6 +111,7 @@ type crConverterInterface interface {
|
||||
// crConverter extends the delegate converter with generic CR conversion behaviour. The delegate will implement the
|
||||
// user defined conversion strategy given in the CustomResourceDefinition.
|
||||
type crConverter struct {
|
||||
convertScale bool
|
||||
converter crConverterInterface
|
||||
validVersions map[schema.GroupVersion]bool
|
||||
clusterScoped bool
|
||||
@ -114,14 +130,23 @@ func (c *crConverter) ConvertFieldLabel(gvk schema.GroupVersionKind, label, valu
|
||||
}
|
||||
|
||||
func (c *crConverter) Convert(in, out, context interface{}) error {
|
||||
// Special-case typed scale conversion if this custom resource supports a scale endpoint
|
||||
if c.convertScale {
|
||||
_, isInScale := in.(*autoscalingv1.Scale)
|
||||
_, isOutScale := out.(*autoscalingv1.Scale)
|
||||
if isInScale || isOutScale {
|
||||
return typedscheme.Scheme.Convert(in, out, context)
|
||||
}
|
||||
}
|
||||
|
||||
unstructIn, ok := in.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("input type %T in not valid for unstructured conversion", in)
|
||||
return fmt.Errorf("input type %T in not valid for unstructured conversion to %T", in, out)
|
||||
}
|
||||
|
||||
unstructOut, ok := out.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("output type %T in not valid for unstructured conversion", out)
|
||||
return fmt.Errorf("output type %T in not valid for unstructured conversion from %T", out, in)
|
||||
}
|
||||
|
||||
outGVK := unstructOut.GroupVersionKind()
|
||||
|
@ -268,7 +268,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
}
|
||||
cr.SetResourceVersion(scale.ResourceVersion)
|
||||
|
||||
obj, _, err = r.store.Update(ctx, cr.GetName(), rest.DefaultUpdatedObjectInfo(cr), createValidation, updateValidation, false, options)
|
||||
obj, _, err = r.store.Update(
|
||||
ctx,
|
||||
cr.GetName(),
|
||||
rest.DefaultUpdatedObjectInfo(cr),
|
||||
toScaleCreateValidation(createValidation, r.specReplicasPath, r.statusReplicasPath, r.labelSelectorPath),
|
||||
toScaleUpdateValidation(updateValidation, r.specReplicasPath, r.statusReplicasPath, r.labelSelectorPath),
|
||||
false,
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -281,6 +289,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
|
||||
return newScale, false, err
|
||||
}
|
||||
|
||||
func toScaleCreateValidation(f rest.ValidateObjectFunc, specReplicasPath, statusReplicasPath, labelSelectorPath string) rest.ValidateObjectFunc {
|
||||
return func(obj runtime.Object) error {
|
||||
scale, _, err := scaleFromCustomResource(obj.(*unstructured.Unstructured), specReplicasPath, statusReplicasPath, labelSelectorPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(scale)
|
||||
}
|
||||
}
|
||||
|
||||
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc, specReplicasPath, statusReplicasPath, labelSelectorPath string) rest.ValidateObjectUpdateFunc {
|
||||
return func(obj, old runtime.Object) error {
|
||||
newScale, _, err := scaleFromCustomResource(obj.(*unstructured.Unstructured), specReplicasPath, statusReplicasPath, labelSelectorPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldScale, _, err := scaleFromCustomResource(old.(*unstructured.Unstructured), specReplicasPath, statusReplicasPath, labelSelectorPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(newScale, oldScale)
|
||||
}
|
||||
}
|
||||
|
||||
// scaleFromCustomResource returns a scale subresource for a customresource and a bool signalling wether
|
||||
// the specReplicas value was found.
|
||||
func scaleFromCustomResource(cr *unstructured.Unstructured, specReplicasPath, statusReplicasPath, labelSelectorPath string) (*autoscalingv1.Scale, bool, error) {
|
||||
@ -310,6 +342,11 @@ func scaleFromCustomResource(cr *unstructured.Unstructured, specReplicasPath, st
|
||||
}
|
||||
|
||||
scale := &autoscalingv1.Scale{
|
||||
// Populate apiVersion and kind so conversion recognizes we are already in the desired GVK and doesn't try to convert
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "autoscaling/v1",
|
||||
Kind: "Scale",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: cr.GetName(),
|
||||
Namespace: cr.GetNamespace(),
|
||||
|
@ -381,6 +381,10 @@ func TestScaleGet(t *testing.T) {
|
||||
}
|
||||
|
||||
want := &autoscalingv1.Scale{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Scale",
|
||||
APIVersion: "autoscaling/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: cr.GetName(),
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
|
Loading…
Reference in New Issue
Block a user