k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/registry/core/replicationcontroller/storage/storage.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // If you make changes to this file, you should also make the corresponding change in ReplicaSet. 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/labels" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 "k8s.io/apimachinery/pkg/util/managedfields" 31 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 32 "k8s.io/apiserver/pkg/registry/generic" 33 genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" 34 "k8s.io/apiserver/pkg/registry/rest" 35 "k8s.io/klog/v2" 36 "k8s.io/kubernetes/pkg/apis/autoscaling" 37 autoscalingv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1" 38 "k8s.io/kubernetes/pkg/apis/autoscaling/validation" 39 api "k8s.io/kubernetes/pkg/apis/core" 40 extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" 41 "k8s.io/kubernetes/pkg/printers" 42 printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" 43 printerstorage "k8s.io/kubernetes/pkg/printers/storage" 44 "k8s.io/kubernetes/pkg/registry/core/replicationcontroller" 45 "sigs.k8s.io/structured-merge-diff/v4/fieldpath" 46 ) 47 48 // ControllerStorage includes dummy storage for Replication Controllers and for Scale subresource. 49 type ControllerStorage struct { 50 Controller *REST 51 Status *StatusREST 52 Scale *ScaleREST 53 } 54 55 // ReplicasPathMappings returns the mappings between each group version and a replicas path 56 func ReplicasPathMappings() managedfields.ResourcePathMappings { 57 return replicasPathInReplicationController 58 } 59 60 // maps a group version to the replicas path in a deployment object 61 var replicasPathInReplicationController = managedfields.ResourcePathMappings{ 62 schema.GroupVersion{Group: "", Version: "v1"}.String(): fieldpath.MakePathOrDie("spec", "replicas"), 63 } 64 65 func NewStorage(optsGetter generic.RESTOptionsGetter) (ControllerStorage, error) { 66 controllerREST, statusREST, err := NewREST(optsGetter) 67 if err != nil { 68 return ControllerStorage{}, err 69 } 70 71 return ControllerStorage{ 72 Controller: controllerREST, 73 Status: statusREST, 74 Scale: &ScaleREST{store: controllerREST.Store}, 75 }, nil 76 } 77 78 type REST struct { 79 *genericregistry.Store 80 } 81 82 // NewREST returns a RESTStorage object that will work against replication controllers. 83 func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) { 84 store := &genericregistry.Store{ 85 NewFunc: func() runtime.Object { return &api.ReplicationController{} }, 86 NewListFunc: func() runtime.Object { return &api.ReplicationControllerList{} }, 87 PredicateFunc: replicationcontroller.MatchController, 88 DefaultQualifiedResource: api.Resource("replicationcontrollers"), 89 SingularQualifiedResource: api.Resource("replicationcontroller"), 90 91 CreateStrategy: replicationcontroller.Strategy, 92 UpdateStrategy: replicationcontroller.Strategy, 93 DeleteStrategy: replicationcontroller.Strategy, 94 ResetFieldsStrategy: replicationcontroller.Strategy, 95 96 TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, 97 } 98 options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: replicationcontroller.GetAttrs} 99 if err := store.CompleteWithOptions(options); err != nil { 100 return nil, nil, err 101 } 102 103 statusStore := *store 104 statusStore.UpdateStrategy = replicationcontroller.StatusStrategy 105 statusStore.ResetFieldsStrategy = replicationcontroller.StatusStrategy 106 107 return &REST{store}, &StatusREST{store: &statusStore}, nil 108 } 109 110 // Implement ShortNamesProvider 111 var _ rest.ShortNamesProvider = &REST{} 112 113 // ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource. 114 func (r *REST) ShortNames() []string { 115 return []string{"rc"} 116 } 117 118 // Implement CategoriesProvider 119 var _ rest.CategoriesProvider = &REST{} 120 121 // Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of. 122 func (r *REST) Categories() []string { 123 return []string{"all"} 124 } 125 126 // StatusREST implements the REST endpoint for changing the status of a replication controller 127 type StatusREST struct { 128 store *genericregistry.Store 129 } 130 131 func (r *StatusREST) New() runtime.Object { 132 return &api.ReplicationController{} 133 } 134 135 // Destroy cleans up resources on shutdown. 136 func (r *StatusREST) Destroy() { 137 // Given that underlying store is shared with REST, 138 // we don't destroy it here explicitly. 139 } 140 141 // Get retrieves the object from the storage. It is required to support Patch. 142 func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { 143 return r.store.Get(ctx, name, options) 144 } 145 146 // Update alters the status subset of an object. 147 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) { 148 // We are explicitly setting forceAllowCreate to false in the call to the underlying storage because 149 // subresources should never allow create on update. 150 return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) 151 } 152 153 // GetResetFields implements rest.ResetFieldsStrategy 154 func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 155 return r.store.GetResetFields() 156 } 157 158 func (r *StatusREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { 159 return r.store.ConvertToTable(ctx, object, tableOptions) 160 } 161 162 type ScaleREST struct { 163 store *genericregistry.Store 164 } 165 166 // ScaleREST implements Patcher 167 var _ = rest.Patcher(&ScaleREST{}) 168 var _ = rest.GroupVersionKindProvider(&ScaleREST{}) 169 170 func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind { 171 switch containingGV { 172 case extensionsv1beta1.SchemeGroupVersion: 173 return extensionsv1beta1.SchemeGroupVersion.WithKind("Scale") 174 default: 175 return autoscalingv1.SchemeGroupVersion.WithKind("Scale") 176 } 177 } 178 179 // New creates a new Scale object 180 func (r *ScaleREST) New() runtime.Object { 181 return &autoscaling.Scale{} 182 } 183 184 // Destroy cleans up resources on shutdown. 185 func (r *ScaleREST) Destroy() { 186 // Given that underlying store is shared with REST, 187 // we don't destroy it here explicitly. 188 } 189 190 func (r *ScaleREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { 191 obj, err := r.store.Get(ctx, name, options) 192 if err != nil { 193 return nil, errors.NewNotFound(autoscaling.Resource("replicationcontrollers/scale"), name) 194 } 195 rc := obj.(*api.ReplicationController) 196 return scaleFromRC(rc), nil 197 } 198 199 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) { 200 obj, _, err := r.store.Update( 201 ctx, 202 name, 203 &scaleUpdatedObjectInfo{name, objInfo}, 204 toScaleCreateValidation(createValidation), 205 toScaleUpdateValidation(updateValidation), 206 false, 207 options, 208 ) 209 if err != nil { 210 return nil, false, err 211 } 212 rc := obj.(*api.ReplicationController) 213 return scaleFromRC(rc), false, nil 214 } 215 216 func (r *ScaleREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { 217 return r.store.ConvertToTable(ctx, object, tableOptions) 218 } 219 220 func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc { 221 return func(ctx context.Context, obj runtime.Object) error { 222 return f(ctx, scaleFromRC(obj.(*api.ReplicationController))) 223 } 224 } 225 226 func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc { 227 return func(ctx context.Context, obj, old runtime.Object) error { 228 return f( 229 ctx, 230 scaleFromRC(obj.(*api.ReplicationController)), 231 scaleFromRC(old.(*api.ReplicationController)), 232 ) 233 } 234 } 235 236 // scaleFromRC returns a scale subresource for a replication controller. 237 func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale { 238 return &autoscaling.Scale{ 239 ObjectMeta: metav1.ObjectMeta{ 240 Name: rc.Name, 241 Namespace: rc.Namespace, 242 UID: rc.UID, 243 ResourceVersion: rc.ResourceVersion, 244 CreationTimestamp: rc.CreationTimestamp, 245 }, 246 Spec: autoscaling.ScaleSpec{ 247 Replicas: rc.Spec.Replicas, 248 }, 249 Status: autoscaling.ScaleStatus{ 250 Replicas: rc.Status.Replicas, 251 Selector: labels.SelectorFromSet(rc.Spec.Selector).String(), 252 }, 253 } 254 } 255 256 // scaleUpdatedObjectInfo transforms existing replication controller -> existing scale -> new scale -> new replication controller 257 type scaleUpdatedObjectInfo struct { 258 name string 259 reqObjInfo rest.UpdatedObjectInfo 260 } 261 262 func (i *scaleUpdatedObjectInfo) Preconditions() *metav1.Preconditions { 263 return i.reqObjInfo.Preconditions() 264 } 265 266 func (i *scaleUpdatedObjectInfo) UpdatedObject(ctx context.Context, oldObj runtime.Object) (runtime.Object, error) { 267 replicationcontroller, ok := oldObj.DeepCopyObject().(*api.ReplicationController) 268 if !ok { 269 return nil, errors.NewBadRequest(fmt.Sprintf("expected existing object type to be ReplicationController, got %T", replicationcontroller)) 270 } 271 // if zero-value, the existing object does not exist 272 if len(replicationcontroller.ResourceVersion) == 0 { 273 return nil, errors.NewNotFound(api.Resource("replicationcontrollers/scale"), i.name) 274 } 275 276 groupVersion := schema.GroupVersion{Group: "", Version: "v1"} 277 if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found { 278 requestGroupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} 279 if _, ok := replicasPathInReplicationController[requestGroupVersion.String()]; ok { 280 groupVersion = requestGroupVersion 281 } else { 282 klog.Fatalf("Unrecognized group/version in request info %q", requestGroupVersion.String()) 283 } 284 } 285 286 managedFieldsHandler := managedfields.NewScaleHandler( 287 replicationcontroller.ManagedFields, 288 groupVersion, 289 replicasPathInReplicationController, 290 ) 291 292 // replicationcontroller -> old scale 293 oldScale := scaleFromRC(replicationcontroller) 294 scaleManagedFields, err := managedFieldsHandler.ToSubresource() 295 if err != nil { 296 return nil, err 297 } 298 oldScale.ManagedFields = scaleManagedFields 299 300 // old scale -> new scale 301 newScaleObj, err := i.reqObjInfo.UpdatedObject(ctx, oldScale) 302 if err != nil { 303 return nil, err 304 } 305 if newScaleObj == nil { 306 return nil, errors.NewBadRequest("nil update passed to Scale") 307 } 308 scale, ok := newScaleObj.(*autoscaling.Scale) 309 if !ok { 310 return nil, errors.NewBadRequest(fmt.Sprintf("expected input object type to be Scale, but %T", newScaleObj)) 311 } 312 313 // validate 314 if errs := validation.ValidateScale(scale); len(errs) > 0 { 315 return nil, errors.NewInvalid(autoscaling.Kind("Scale"), replicationcontroller.Name, errs) 316 } 317 318 // validate precondition if specified (resourceVersion matching is handled by storage) 319 if len(scale.UID) > 0 && scale.UID != replicationcontroller.UID { 320 return nil, errors.NewConflict( 321 api.Resource("replicationcontrollers/scale"), 322 replicationcontroller.Name, 323 fmt.Errorf("Precondition failed: UID in precondition: %v, UID in object meta: %v", scale.UID, replicationcontroller.UID), 324 ) 325 } 326 327 // move replicas/resourceVersion fields to object and return 328 replicationcontroller.Spec.Replicas = scale.Spec.Replicas 329 replicationcontroller.ResourceVersion = scale.ResourceVersion 330 331 updatedEntries, err := managedFieldsHandler.ToParent(scale.ManagedFields) 332 if err != nil { 333 return nil, err 334 } 335 replicationcontroller.ManagedFields = updatedEntries 336 337 return replicationcontroller, nil 338 }