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