...

Source file src/k8s.io/kubernetes/pkg/registry/core/replicationcontroller/storage/storage.go

Documentation: k8s.io/kubernetes/pkg/registry/core/replicationcontroller/storage

     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  }
   339  

View as plain text