Skip to content

Commit dced88e

Browse files
committed
Fix scale subresource when used with admission webhooks
1 parent 3bbd9b2 commit dced88e

File tree

9 files changed

+218
-8
lines changed

9 files changed

+218
-8
lines changed

pkg/registry/apps/deployment/storage/storage.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
293293

294294
deployment.Spec.Replicas = scale.Spec.Replicas
295295
deployment.ResourceVersion = scale.ResourceVersion
296-
obj, _, err = r.store.Update(ctx, deployment.Name, rest.DefaultUpdatedObjectInfo(deployment), createValidation, updateValidation, false, options)
296+
obj, _, err = r.store.Update(
297+
ctx,
298+
deployment.Name,
299+
rest.DefaultUpdatedObjectInfo(deployment),
300+
toScaleCreateValidation(createValidation),
301+
toScaleUpdateValidation(updateValidation),
302+
false,
303+
options,
304+
)
297305
if err != nil {
298306
return nil, false, err
299307
}
@@ -305,6 +313,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
305313
return newScale, false, nil
306314
}
307315

316+
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
317+
return func(obj runtime.Object) error {
318+
scale, err := scaleFromDeployment(obj.(*apps.Deployment))
319+
if err != nil {
320+
return err
321+
}
322+
return f(scale)
323+
}
324+
}
325+
326+
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
327+
return func(obj, old runtime.Object) error {
328+
newScale, err := scaleFromDeployment(obj.(*apps.Deployment))
329+
if err != nil {
330+
return err
331+
}
332+
oldScale, err := scaleFromDeployment(old.(*apps.Deployment))
333+
if err != nil {
334+
return err
335+
}
336+
return f(newScale, oldScale)
337+
}
338+
}
339+
308340
// scaleFromDeployment returns a scale subresource for a deployment.
309341
func scaleFromDeployment(deployment *apps.Deployment) (*autoscaling.Scale, error) {
310342
selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)

pkg/registry/apps/replicaset/storage/storage.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
201201

202202
rs.Spec.Replicas = scale.Spec.Replicas
203203
rs.ResourceVersion = scale.ResourceVersion
204-
obj, _, err = r.store.Update(ctx, rs.Name, rest.DefaultUpdatedObjectInfo(rs), createValidation, updateValidation, false, options)
204+
obj, _, err = r.store.Update(
205+
ctx,
206+
rs.Name,
207+
rest.DefaultUpdatedObjectInfo(rs),
208+
toScaleCreateValidation(createValidation),
209+
toScaleUpdateValidation(updateValidation),
210+
false,
211+
options,
212+
)
205213
if err != nil {
206214
return nil, false, err
207215
}
@@ -213,6 +221,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
213221
return newScale, false, err
214222
}
215223

224+
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
225+
return func(obj runtime.Object) error {
226+
scale, err := scaleFromReplicaSet(obj.(*apps.ReplicaSet))
227+
if err != nil {
228+
return err
229+
}
230+
return f(scale)
231+
}
232+
}
233+
234+
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
235+
return func(obj, old runtime.Object) error {
236+
newScale, err := scaleFromReplicaSet(obj.(*apps.ReplicaSet))
237+
if err != nil {
238+
return err
239+
}
240+
oldScale, err := scaleFromReplicaSet(old.(*apps.ReplicaSet))
241+
if err != nil {
242+
return err
243+
}
244+
return f(newScale, oldScale)
245+
}
246+
}
247+
216248
// scaleFromReplicaSet returns a scale subresource for a replica set.
217249
func scaleFromReplicaSet(rs *apps.ReplicaSet) (*autoscaling.Scale, error) {
218250
selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)

pkg/registry/apps/statefulset/storage/storage.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
188188

189189
ss.Spec.Replicas = scale.Spec.Replicas
190190
ss.ResourceVersion = scale.ResourceVersion
191-
obj, _, err = r.store.Update(ctx, ss.Name, rest.DefaultUpdatedObjectInfo(ss), createValidation, updateValidation, false, options)
191+
obj, _, err = r.store.Update(
192+
ctx,
193+
ss.Name,
194+
rest.DefaultUpdatedObjectInfo(ss),
195+
toScaleCreateValidation(createValidation),
196+
toScaleUpdateValidation(updateValidation),
197+
false,
198+
options,
199+
)
192200
if err != nil {
193201
return nil, false, err
194202
}
@@ -200,6 +208,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
200208
return newScale, false, err
201209
}
202210

211+
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
212+
return func(obj runtime.Object) error {
213+
scale, err := scaleFromStatefulSet(obj.(*apps.StatefulSet))
214+
if err != nil {
215+
return err
216+
}
217+
return f(scale)
218+
}
219+
}
220+
221+
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
222+
return func(obj, old runtime.Object) error {
223+
newScale, err := scaleFromStatefulSet(obj.(*apps.StatefulSet))
224+
if err != nil {
225+
return err
226+
}
227+
oldScale, err := scaleFromStatefulSet(old.(*apps.StatefulSet))
228+
if err != nil {
229+
return err
230+
}
231+
return f(newScale, oldScale)
232+
}
233+
}
234+
203235
// scaleFromStatefulSet returns a scale subresource for a statefulset.
204236
func scaleFromStatefulSet(ss *apps.StatefulSet) (*autoscaling.Scale, error) {
205237
selector, err := metav1.LabelSelectorAsSelector(ss.Spec.Selector)

pkg/registry/core/replicationcontroller/storage/storage.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,37 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
183183

184184
rc.Spec.Replicas = scale.Spec.Replicas
185185
rc.ResourceVersion = scale.ResourceVersion
186-
obj, _, err = r.store.Update(ctx, rc.Name, rest.DefaultUpdatedObjectInfo(rc), createValidation, updateValidation, false, options)
186+
obj, _, err = r.store.Update(
187+
ctx,
188+
rc.Name,
189+
rest.DefaultUpdatedObjectInfo(rc),
190+
toScaleCreateValidation(createValidation),
191+
toScaleUpdateValidation(updateValidation),
192+
false,
193+
options,
194+
)
187195
if err != nil {
188196
return nil, false, err
189197
}
190198
rc = obj.(*api.ReplicationController)
191199
return scaleFromRC(rc), false, nil
192200
}
193201

202+
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
203+
return func(obj runtime.Object) error {
204+
return f(scaleFromRC(obj.(*api.ReplicationController)))
205+
}
206+
}
207+
208+
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
209+
return func(obj, old runtime.Object) error {
210+
return f(
211+
scaleFromRC(obj.(*api.ReplicationController)),
212+
scaleFromRC(old.(*api.ReplicationController)),
213+
)
214+
}
215+
}
216+
194217
// scaleFromRC returns a scale subresource for a replication controller.
195218
func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale {
196219
return &autoscaling.Scale{

pkg/registry/extensions/controller/storage/storage.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,37 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
9595

9696
rc.Spec.Replicas = scale.Spec.Replicas
9797
rc.ResourceVersion = scale.ResourceVersion
98-
obj, _, err = r.store.Update(ctx, rc.Name, rest.DefaultUpdatedObjectInfo(rc), createValidation, updateValidation, false, options)
98+
obj, _, err = r.store.Update(
99+
ctx,
100+
rc.Name,
101+
rest.DefaultUpdatedObjectInfo(rc),
102+
toScaleCreateValidation(createValidation),
103+
toScaleUpdateValidation(updateValidation),
104+
false,
105+
options,
106+
)
99107
if err != nil {
100108
return nil, false, errors.NewConflict(extensions.Resource("replicationcontrollers/scale"), scale.Name, err)
101109
}
102110
rc = obj.(*api.ReplicationController)
103111
return scaleFromRC(rc), false, nil
104112
}
105113

114+
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
115+
return func(obj runtime.Object) error {
116+
return f(scaleFromRC(obj.(*api.ReplicationController)))
117+
}
118+
}
119+
120+
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
121+
return func(obj, old runtime.Object) error {
122+
return f(
123+
scaleFromRC(obj.(*api.ReplicationController)),
124+
scaleFromRC(old.(*api.ReplicationController)),
125+
)
126+
}
127+
}
128+
106129
// scaleFromRC returns a scale subresource for a replication controller.
107130
func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale {
108131
return &autoscaling.Scale{

staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/conversion/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ go_library(
1212
importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver/conversion",
1313
visibility = ["//visibility:public"],
1414
deps = [
15+
"//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
1516
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
1617
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
1718
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
@@ -22,6 +23,7 @@ go_library(
2223
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
2324
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
2425
"//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library",
26+
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
2527
"//staging/src/k8s.io/client-go/rest:go_default_library",
2628
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
2729
],

staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/conversion/converter.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ package conversion
1919
import (
2020
"fmt"
2121

22+
autoscalingv1 "k8s.io/api/autoscaling/v1"
2223
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
2324
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
2425
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2526
"k8s.io/apimachinery/pkg/runtime"
2627
"k8s.io/apimachinery/pkg/runtime/schema"
2728
utilfeature "k8s.io/apiserver/pkg/util/feature"
2829
"k8s.io/apiserver/pkg/util/webhook"
30+
typedscheme "k8s.io/client-go/kubernetes/scheme"
2931
)
3032

3133
// CRConverterFactory is the factory for all CR converters.
@@ -77,7 +79,20 @@ func (m *CRConverterFactory) NewConverter(crd *apiextensions.CustomResourceDefin
7779
default:
7880
return nil, nil, fmt.Errorf("unknown conversion strategy %q for CRD %s", crd.Spec.Conversion.Strategy, crd.Name)
7981
}
82+
83+
// Determine whether we should expect to be asked to "convert" autoscaling/v1 Scale types
84+
convertScale := false
85+
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) {
86+
convertScale = crd.Spec.Subresources != nil && crd.Spec.Subresources.Scale != nil
87+
for _, version := range crd.Spec.Versions {
88+
if version.Subresources != nil && version.Subresources.Scale != nil {
89+
convertScale = true
90+
}
91+
}
92+
}
93+
8094
unsafe = &crConverter{
95+
convertScale: convertScale,
8196
validVersions: validVersions,
8297
clusterScoped: crd.Spec.Scope == apiextensions.ClusterScoped,
8398
converter: converter,
@@ -96,6 +111,7 @@ type crConverterInterface interface {
96111
// crConverter extends the delegate converter with generic CR conversion behaviour. The delegate will implement the
97112
// user defined conversion strategy given in the CustomResourceDefinition.
98113
type crConverter struct {
114+
convertScale bool
99115
converter crConverterInterface
100116
validVersions map[schema.GroupVersion]bool
101117
clusterScoped bool
@@ -114,14 +130,23 @@ func (c *crConverter) ConvertFieldLabel(gvk schema.GroupVersionKind, label, valu
114130
}
115131

116132
func (c *crConverter) Convert(in, out, context interface{}) error {
133+
// Special-case typed scale conversion if this custom resource supports a scale endpoint
134+
if c.convertScale {
135+
_, isInScale := in.(*autoscalingv1.Scale)
136+
_, isOutScale := out.(*autoscalingv1.Scale)
137+
if isInScale || isOutScale {
138+
return typedscheme.Scheme.Convert(in, out, context)
139+
}
140+
}
141+
117142
unstructIn, ok := in.(*unstructured.Unstructured)
118143
if !ok {
119-
return fmt.Errorf("input type %T in not valid for unstructured conversion", in)
144+
return fmt.Errorf("input type %T in not valid for unstructured conversion to %T", in, out)
120145
}
121146

122147
unstructOut, ok := out.(*unstructured.Unstructured)
123148
if !ok {
124-
return fmt.Errorf("output type %T in not valid for unstructured conversion", out)
149+
return fmt.Errorf("output type %T in not valid for unstructured conversion from %T", out, in)
125150
}
126151

127152
outGVK := unstructOut.GroupVersionKind()

staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
268268
}
269269
cr.SetResourceVersion(scale.ResourceVersion)
270270

271-
obj, _, err = r.store.Update(ctx, cr.GetName(), rest.DefaultUpdatedObjectInfo(cr), createValidation, updateValidation, false, options)
271+
obj, _, err = r.store.Update(
272+
ctx,
273+
cr.GetName(),
274+
rest.DefaultUpdatedObjectInfo(cr),
275+
toScaleCreateValidation(createValidation, r.specReplicasPath, r.statusReplicasPath, r.labelSelectorPath),
276+
toScaleUpdateValidation(updateValidation, r.specReplicasPath, r.statusReplicasPath, r.labelSelectorPath),
277+
false,
278+
options,
279+
)
272280
if err != nil {
273281
return nil, false, err
274282
}
@@ -281,6 +289,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
281289
return newScale, false, err
282290
}
283291

292+
func toScaleCreateValidation(f rest.ValidateObjectFunc, specReplicasPath, statusReplicasPath, labelSelectorPath string) rest.ValidateObjectFunc {
293+
return func(obj runtime.Object) error {
294+
scale, _, err := scaleFromCustomResource(obj.(*unstructured.Unstructured), specReplicasPath, statusReplicasPath, labelSelectorPath)
295+
if err != nil {
296+
return err
297+
}
298+
return f(scale)
299+
}
300+
}
301+
302+
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc, specReplicasPath, statusReplicasPath, labelSelectorPath string) rest.ValidateObjectUpdateFunc {
303+
return func(obj, old runtime.Object) error {
304+
newScale, _, err := scaleFromCustomResource(obj.(*unstructured.Unstructured), specReplicasPath, statusReplicasPath, labelSelectorPath)
305+
if err != nil {
306+
return err
307+
}
308+
oldScale, _, err := scaleFromCustomResource(old.(*unstructured.Unstructured), specReplicasPath, statusReplicasPath, labelSelectorPath)
309+
if err != nil {
310+
return err
311+
}
312+
return f(newScale, oldScale)
313+
}
314+
}
315+
284316
// scaleFromCustomResource returns a scale subresource for a customresource and a bool signalling wether
285317
// the specReplicas value was found.
286318
func scaleFromCustomResource(cr *unstructured.Unstructured, specReplicasPath, statusReplicasPath, labelSelectorPath string) (*autoscalingv1.Scale, bool, error) {
@@ -310,6 +342,11 @@ func scaleFromCustomResource(cr *unstructured.Unstructured, specReplicasPath, st
310342
}
311343

312344
scale := &autoscalingv1.Scale{
345+
// Populate apiVersion and kind so conversion recognizes we are already in the desired GVK and doesn't try to convert
346+
TypeMeta: metav1.TypeMeta{
347+
APIVersion: "autoscaling/v1",
348+
Kind: "Scale",
349+
},
313350
ObjectMeta: metav1.ObjectMeta{
314351
Name: cr.GetName(),
315352
Namespace: cr.GetNamespace(),

staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,10 @@ func TestScaleGet(t *testing.T) {
381381
}
382382

383383
want := &autoscalingv1.Scale{
384+
TypeMeta: metav1.TypeMeta{
385+
Kind: "Scale",
386+
APIVersion: "autoscaling/v1",
387+
},
384388
ObjectMeta: metav1.ObjectMeta{
385389
Name: cr.GetName(),
386390
Namespace: metav1.NamespaceDefault,

0 commit comments

Comments
 (0)