...

Source file src/k8s.io/kubernetes/pkg/registry/apps/replicaset/storage/storage.go

Documentation: k8s.io/kubernetes/pkg/registry/apps/replicaset/storage

     1  /*
     2  Copyright 2016 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 ReplicationController.
    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  // ReplicaSetStorage includes dummy storage for ReplicaSets and for Scale subresource.
    50  type ReplicaSetStorage struct {
    51  	ReplicaSet *REST
    52  	Status     *StatusREST
    53  	Scale      *ScaleREST
    54  }
    55  
    56  // ReplicasPathMappings returns the mappings between each group version and a replicas path
    57  func ReplicasPathMappings() managedfields.ResourcePathMappings {
    58  	return replicasPathInReplicaSet
    59  }
    60  
    61  // maps a group version to the replicas path in a replicaset object
    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  // NewStorage returns new instance of ReplicaSetStorage.
    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  // REST implements a RESTStorage for ReplicaSet.
    82  type REST struct {
    83  	*genericregistry.Store
    84  }
    85  
    86  // NewREST returns a RESTStorage object that will work against ReplicaSet.
    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  // Implement ShortNamesProvider
   115  var _ rest.ShortNamesProvider = &REST{}
   116  
   117  // ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
   118  func (r *REST) ShortNames() []string {
   119  	return []string{"rs"}
   120  }
   121  
   122  // Implement CategoriesProvider
   123  var _ rest.CategoriesProvider = &REST{}
   124  
   125  // Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of.
   126  func (r *REST) Categories() []string {
   127  	return []string{"all"}
   128  }
   129  
   130  // StatusREST implements the REST endpoint for changing the status of a ReplicaSet
   131  type StatusREST struct {
   132  	store *genericregistry.Store
   133  }
   134  
   135  // New returns empty ReplicaSet object.
   136  func (r *StatusREST) New() runtime.Object {
   137  	return &apps.ReplicaSet{}
   138  }
   139  
   140  // Destroy cleans up resources on shutdown.
   141  func (r *StatusREST) Destroy() {
   142  	// Given that underlying store is shared with REST,
   143  	// we don't destroy it here explicitly.
   144  }
   145  
   146  // Get retrieves the object from the storage. It is required to support Patch.
   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  // Update alters the status subset of an object.
   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  	// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
   154  	// subresources should never allow create on update.
   155  	return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
   156  }
   157  
   158  // GetResetFields implements rest.ResetFieldsStrategy
   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  // ScaleREST implements a Scale for ReplicaSet.
   168  type ScaleREST struct {
   169  	store *genericregistry.Store
   170  }
   171  
   172  // ScaleREST implements Patcher
   173  var _ = rest.Patcher(&ScaleREST{})
   174  var _ = rest.GroupVersionKindProvider(&ScaleREST{})
   175  
   176  // GroupVersionKind returns GroupVersionKind for ReplicaSet Scale object
   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  // New creates a new Scale object
   191  func (r *ScaleREST) New() runtime.Object {
   192  	return &autoscaling.Scale{}
   193  }
   194  
   195  // Destroy cleans up resources on shutdown.
   196  func (r *ScaleREST) Destroy() {
   197  	// Given that underlying store is shared with REST,
   198  	// we don't destroy it here explicitly.
   199  }
   200  
   201  // Get retrieves object from Scale storage.
   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  // Update alters scale subset of ReplicaSet object.
   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  // scaleFromReplicaSet returns a scale subresource for a replica set.
   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  		// TODO: Create a variant of ObjectMeta type that only contains the fields below.
   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  // scaleUpdatedObjectInfo transforms existing replicaset -> existing scale -> new scale -> new replicaset
   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  	// if zero-value, the existing object does not exist
   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  	// replicaset -> old scale
   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  	// old scale -> new scale
   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  	// validate
   352  	if errs := autoscalingvalidation.ValidateScale(scale); len(errs) > 0 {
   353  		return nil, errors.NewInvalid(autoscaling.Kind("Scale"), replicaset.Name, errs)
   354  	}
   355  
   356  	// validate precondition if specified (resourceVersion matching is handled by storage)
   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  	// move replicas/resourceVersion fields to object and return
   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