1
16
17
18
19 package storage
20
21 import (
22 "context"
23 "fmt"
24
25 "k8s.io/apimachinery/pkg/api/errors"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "k8s.io/apimachinery/pkg/util/managedfields"
30 genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
31 "k8s.io/apiserver/pkg/registry/generic"
32 genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
33 "k8s.io/apiserver/pkg/registry/rest"
34 "k8s.io/klog/v2"
35 "k8s.io/kubernetes/pkg/apis/apps"
36 appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
37 appsv1beta2 "k8s.io/kubernetes/pkg/apis/apps/v1beta2"
38 "k8s.io/kubernetes/pkg/apis/autoscaling"
39 autoscalingv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
40 autoscalingvalidation "k8s.io/kubernetes/pkg/apis/autoscaling/validation"
41 extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
42 "k8s.io/kubernetes/pkg/printers"
43 printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
44 printerstorage "k8s.io/kubernetes/pkg/printers/storage"
45 "k8s.io/kubernetes/pkg/registry/apps/replicaset"
46 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
47 )
48
49
50 type ReplicaSetStorage struct {
51 ReplicaSet *REST
52 Status *StatusREST
53 Scale *ScaleREST
54 }
55
56
57 func ReplicasPathMappings() managedfields.ResourcePathMappings {
58 return replicasPathInReplicaSet
59 }
60
61
62 var replicasPathInReplicaSet = managedfields.ResourcePathMappings{
63 schema.GroupVersion{Group: "apps", Version: "v1beta2"}.String(): fieldpath.MakePathOrDie("spec", "replicas"),
64 schema.GroupVersion{Group: "apps", Version: "v1"}.String(): fieldpath.MakePathOrDie("spec", "replicas"),
65 }
66
67
68 func NewStorage(optsGetter generic.RESTOptionsGetter) (ReplicaSetStorage, error) {
69 replicaSetRest, replicaSetStatusRest, err := NewREST(optsGetter)
70 if err != nil {
71 return ReplicaSetStorage{}, err
72 }
73
74 return ReplicaSetStorage{
75 ReplicaSet: replicaSetRest,
76 Status: replicaSetStatusRest,
77 Scale: &ScaleREST{store: replicaSetRest.Store},
78 }, nil
79 }
80
81
82 type REST struct {
83 *genericregistry.Store
84 }
85
86
87 func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
88 store := &genericregistry.Store{
89 NewFunc: func() runtime.Object { return &apps.ReplicaSet{} },
90 NewListFunc: func() runtime.Object { return &apps.ReplicaSetList{} },
91 PredicateFunc: replicaset.MatchReplicaSet,
92 DefaultQualifiedResource: apps.Resource("replicasets"),
93 SingularQualifiedResource: apps.Resource("replicaset"),
94
95 CreateStrategy: replicaset.Strategy,
96 UpdateStrategy: replicaset.Strategy,
97 DeleteStrategy: replicaset.Strategy,
98 ResetFieldsStrategy: replicaset.Strategy,
99
100 TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
101 }
102 options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: replicaset.GetAttrs}
103 if err := store.CompleteWithOptions(options); err != nil {
104 return nil, nil, err
105 }
106
107 statusStore := *store
108 statusStore.UpdateStrategy = replicaset.StatusStrategy
109 statusStore.ResetFieldsStrategy = replicaset.StatusStrategy
110
111 return &REST{store}, &StatusREST{store: &statusStore}, nil
112 }
113
114
115 var _ rest.ShortNamesProvider = &REST{}
116
117
118 func (r *REST) ShortNames() []string {
119 return []string{"rs"}
120 }
121
122
123 var _ rest.CategoriesProvider = &REST{}
124
125
126 func (r *REST) Categories() []string {
127 return []string{"all"}
128 }
129
130
131 type StatusREST struct {
132 store *genericregistry.Store
133 }
134
135
136 func (r *StatusREST) New() runtime.Object {
137 return &apps.ReplicaSet{}
138 }
139
140
141 func (r *StatusREST) Destroy() {
142
143
144 }
145
146
147 func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
148 return r.store.Get(ctx, name, options)
149 }
150
151
152 func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
153
154
155 return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
156 }
157
158
159 func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
160 return r.store.GetResetFields()
161 }
162
163 func (r *StatusREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
164 return r.store.ConvertToTable(ctx, object, tableOptions)
165 }
166
167
168 type ScaleREST struct {
169 store *genericregistry.Store
170 }
171
172
173 var _ = rest.Patcher(&ScaleREST{})
174 var _ = rest.GroupVersionKindProvider(&ScaleREST{})
175
176
177 func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
178 switch containingGV {
179 case extensionsv1beta1.SchemeGroupVersion:
180 return extensionsv1beta1.SchemeGroupVersion.WithKind("Scale")
181 case appsv1beta1.SchemeGroupVersion:
182 return appsv1beta1.SchemeGroupVersion.WithKind("Scale")
183 case appsv1beta2.SchemeGroupVersion:
184 return appsv1beta2.SchemeGroupVersion.WithKind("Scale")
185 default:
186 return autoscalingv1.SchemeGroupVersion.WithKind("Scale")
187 }
188 }
189
190
191 func (r *ScaleREST) New() runtime.Object {
192 return &autoscaling.Scale{}
193 }
194
195
196 func (r *ScaleREST) Destroy() {
197
198
199 }
200
201
202 func (r *ScaleREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
203 obj, err := r.store.Get(ctx, name, options)
204 if err != nil {
205 return nil, errors.NewNotFound(apps.Resource("replicasets/scale"), name)
206 }
207 rs := obj.(*apps.ReplicaSet)
208 scale, err := scaleFromReplicaSet(rs)
209 if err != nil {
210 return nil, errors.NewBadRequest(fmt.Sprintf("%v", err))
211 }
212 return scale, err
213 }
214
215
216 func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
217 obj, _, err := r.store.Update(
218 ctx,
219 name,
220 &scaleUpdatedObjectInfo{name, objInfo},
221 toScaleCreateValidation(createValidation),
222 toScaleUpdateValidation(updateValidation),
223 false,
224 options,
225 )
226 if err != nil {
227 return nil, false, err
228 }
229 rs := obj.(*apps.ReplicaSet)
230 newScale, err := scaleFromReplicaSet(rs)
231 if err != nil {
232 return nil, false, errors.NewBadRequest(fmt.Sprintf("%v", err))
233 }
234 return newScale, false, err
235 }
236
237 func (r *ScaleREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
238 return r.store.ConvertToTable(ctx, object, tableOptions)
239 }
240
241 func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
242 return func(ctx context.Context, obj runtime.Object) error {
243 scale, err := scaleFromReplicaSet(obj.(*apps.ReplicaSet))
244 if err != nil {
245 return err
246 }
247 return f(ctx, scale)
248 }
249 }
250
251 func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
252 return func(ctx context.Context, obj, old runtime.Object) error {
253 newScale, err := scaleFromReplicaSet(obj.(*apps.ReplicaSet))
254 if err != nil {
255 return err
256 }
257 oldScale, err := scaleFromReplicaSet(old.(*apps.ReplicaSet))
258 if err != nil {
259 return err
260 }
261 return f(ctx, newScale, oldScale)
262 }
263 }
264
265
266 func scaleFromReplicaSet(rs *apps.ReplicaSet) (*autoscaling.Scale, error) {
267 selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)
268 if err != nil {
269 return nil, err
270 }
271 return &autoscaling.Scale{
272
273 ObjectMeta: metav1.ObjectMeta{
274 Name: rs.Name,
275 Namespace: rs.Namespace,
276 UID: rs.UID,
277 ResourceVersion: rs.ResourceVersion,
278 CreationTimestamp: rs.CreationTimestamp,
279 },
280 Spec: autoscaling.ScaleSpec{
281 Replicas: rs.Spec.Replicas,
282 },
283 Status: autoscaling.ScaleStatus{
284 Replicas: rs.Status.Replicas,
285 Selector: selector.String(),
286 },
287 }, nil
288 }
289
290
291 type scaleUpdatedObjectInfo struct {
292 name string
293 reqObjInfo rest.UpdatedObjectInfo
294 }
295
296 func (i *scaleUpdatedObjectInfo) Preconditions() *metav1.Preconditions {
297 return i.reqObjInfo.Preconditions()
298 }
299
300 func (i *scaleUpdatedObjectInfo) UpdatedObject(ctx context.Context, oldObj runtime.Object) (runtime.Object, error) {
301 replicaset, ok := oldObj.DeepCopyObject().(*apps.ReplicaSet)
302 if !ok {
303 return nil, errors.NewBadRequest(fmt.Sprintf("expected existing object type to be ReplicaSet, got %T", replicaset))
304 }
305
306 if len(replicaset.ResourceVersion) == 0 {
307 return nil, errors.NewNotFound(apps.Resource("replicasets/scale"), i.name)
308 }
309
310 groupVersion := schema.GroupVersion{Group: "apps", Version: "v1"}
311 if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
312 requestGroupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
313 if _, ok := replicasPathInReplicaSet[requestGroupVersion.String()]; ok {
314 groupVersion = requestGroupVersion
315 } else {
316 klog.Fatalf("Unrecognized group/version in request info %q", requestGroupVersion.String())
317 }
318 }
319
320 managedFieldsHandler := managedfields.NewScaleHandler(
321 replicaset.ManagedFields,
322 groupVersion,
323 replicasPathInReplicaSet,
324 )
325
326
327 oldScale, err := scaleFromReplicaSet(replicaset)
328 if err != nil {
329 return nil, err
330 }
331
332 scaleManagedFields, err := managedFieldsHandler.ToSubresource()
333 if err != nil {
334 return nil, err
335 }
336 oldScale.ManagedFields = scaleManagedFields
337
338
339 newScaleObj, err := i.reqObjInfo.UpdatedObject(ctx, oldScale)
340 if err != nil {
341 return nil, err
342 }
343 if newScaleObj == nil {
344 return nil, errors.NewBadRequest("nil update passed to Scale")
345 }
346 scale, ok := newScaleObj.(*autoscaling.Scale)
347 if !ok {
348 return nil, errors.NewBadRequest(fmt.Sprintf("expected input object type to be Scale, but %T", newScaleObj))
349 }
350
351
352 if errs := autoscalingvalidation.ValidateScale(scale); len(errs) > 0 {
353 return nil, errors.NewInvalid(autoscaling.Kind("Scale"), replicaset.Name, errs)
354 }
355
356
357 if len(scale.UID) > 0 && scale.UID != replicaset.UID {
358 return nil, errors.NewConflict(
359 apps.Resource("replicasets/scale"),
360 replicaset.Name,
361 fmt.Errorf("Precondition failed: UID in precondition: %v, UID in object meta: %v", scale.UID, replicaset.UID),
362 )
363 }
364
365
366 replicaset.Spec.Replicas = scale.Spec.Replicas
367 replicaset.ResourceVersion = scale.ResourceVersion
368
369 updatedEntries, err := managedFieldsHandler.ToParent(scale.ManagedFields)
370 if err != nil {
371 return nil, err
372 }
373 replicaset.ManagedFields = updatedEntries
374
375 return replicaset, nil
376 }
377
View as plain text