...

Source file src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go

Documentation: k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition

     1  /*
     2  Copyright 2017 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  package customresourcedefinition
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
    24  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/apiserver/pkg/registry/generic"
    28  	genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
    29  	"k8s.io/apiserver/pkg/registry/rest"
    30  	"k8s.io/apiserver/pkg/storage"
    31  	storageerr "k8s.io/apiserver/pkg/storage/errors"
    32  	"k8s.io/apiserver/pkg/util/dryrun"
    33  	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
    34  )
    35  
    36  // rest implements a RESTStorage for API services against etcd
    37  type REST struct {
    38  	*genericregistry.Store
    39  }
    40  
    41  // NewREST returns a RESTStorage object that will work against API services.
    42  func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*REST, error) {
    43  	strategy := NewStrategy(scheme)
    44  
    45  	store := &genericregistry.Store{
    46  		NewFunc:                   func() runtime.Object { return &apiextensions.CustomResourceDefinition{} },
    47  		NewListFunc:               func() runtime.Object { return &apiextensions.CustomResourceDefinitionList{} },
    48  		PredicateFunc:             MatchCustomResourceDefinition,
    49  		DefaultQualifiedResource:  apiextensions.Resource("customresourcedefinitions"),
    50  		SingularQualifiedResource: apiextensions.Resource("customresourcedefinition"),
    51  
    52  		CreateStrategy:      strategy,
    53  		UpdateStrategy:      strategy,
    54  		DeleteStrategy:      strategy,
    55  		ResetFieldsStrategy: strategy,
    56  
    57  		// TODO: define table converter that exposes more than name/creation timestamp
    58  		TableConvertor: rest.NewDefaultTableConvertor(apiextensions.Resource("customresourcedefinitions")),
    59  	}
    60  	options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
    61  	if err := store.CompleteWithOptions(options); err != nil {
    62  		return nil, err
    63  	}
    64  	return &REST{store}, nil
    65  }
    66  
    67  // Implement ShortNamesProvider
    68  var _ rest.ShortNamesProvider = &REST{}
    69  
    70  // ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
    71  func (r *REST) ShortNames() []string {
    72  	return []string{"crd", "crds"}
    73  }
    74  
    75  // Implement CategoriesProvider
    76  var _ rest.CategoriesProvider = &REST{}
    77  
    78  // Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of.
    79  func (r *REST) Categories() []string {
    80  	return []string{"api-extensions"}
    81  }
    82  
    83  // Delete adds the CRD finalizer to the list
    84  func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
    85  	obj, err := r.Get(ctx, name, &metav1.GetOptions{})
    86  	if err != nil {
    87  		return nil, false, err
    88  	}
    89  
    90  	crd := obj.(*apiextensions.CustomResourceDefinition)
    91  
    92  	// Ensure we have a UID precondition
    93  	if options == nil {
    94  		options = metav1.NewDeleteOptions(0)
    95  	}
    96  	if options.Preconditions == nil {
    97  		options.Preconditions = &metav1.Preconditions{}
    98  	}
    99  	if options.Preconditions.UID == nil {
   100  		options.Preconditions.UID = &crd.UID
   101  	} else if *options.Preconditions.UID != crd.UID {
   102  		err = apierrors.NewConflict(
   103  			apiextensions.Resource("customresourcedefinitions"),
   104  			name,
   105  			fmt.Errorf("Precondition failed: UID in precondition: %v, UID in object meta: %v", *options.Preconditions.UID, crd.UID),
   106  		)
   107  		return nil, false, err
   108  	}
   109  	if options.Preconditions.ResourceVersion != nil && *options.Preconditions.ResourceVersion != crd.ResourceVersion {
   110  		err = apierrors.NewConflict(
   111  			apiextensions.Resource("customresourcedefinitions"),
   112  			name,
   113  			fmt.Errorf("Precondition failed: ResourceVersion in precondition: %v, ResourceVersion in object meta: %v", *options.Preconditions.ResourceVersion, crd.ResourceVersion),
   114  		)
   115  		return nil, false, err
   116  	}
   117  
   118  	// upon first request to delete, add our finalizer and then delegate
   119  	if crd.DeletionTimestamp.IsZero() {
   120  		key, err := r.Store.KeyFunc(ctx, name)
   121  		if err != nil {
   122  			return nil, false, err
   123  		}
   124  
   125  		preconditions := storage.Preconditions{UID: options.Preconditions.UID, ResourceVersion: options.Preconditions.ResourceVersion}
   126  
   127  		out := r.Store.NewFunc()
   128  		err = r.Store.Storage.GuaranteedUpdate(
   129  			ctx, key, out, false, &preconditions,
   130  			storage.SimpleUpdate(func(existing runtime.Object) (runtime.Object, error) {
   131  				existingCRD, ok := existing.(*apiextensions.CustomResourceDefinition)
   132  				if !ok {
   133  					// wrong type
   134  					return nil, fmt.Errorf("expected *apiextensions.CustomResourceDefinition, got %v", existing)
   135  				}
   136  				if err := deleteValidation(ctx, existingCRD); err != nil {
   137  					return nil, err
   138  				}
   139  
   140  				// Set the deletion timestamp if needed
   141  				if existingCRD.DeletionTimestamp.IsZero() {
   142  					now := metav1.Now()
   143  					existingCRD.DeletionTimestamp = &now
   144  				}
   145  
   146  				if !apiextensions.CRDHasFinalizer(existingCRD, apiextensions.CustomResourceCleanupFinalizer) {
   147  					existingCRD.Finalizers = append(existingCRD.Finalizers, apiextensions.CustomResourceCleanupFinalizer)
   148  				}
   149  				// update the status condition too
   150  				apiextensions.SetCRDCondition(existingCRD, apiextensions.CustomResourceDefinitionCondition{
   151  					Type:    apiextensions.Terminating,
   152  					Status:  apiextensions.ConditionTrue,
   153  					Reason:  "InstanceDeletionPending",
   154  					Message: "CustomResourceDefinition marked for deletion; CustomResource deletion will begin soon",
   155  				})
   156  				return existingCRD, nil
   157  			}),
   158  			dryrun.IsDryRun(options.DryRun),
   159  			nil,
   160  		)
   161  
   162  		if err != nil {
   163  			err = storageerr.InterpretGetError(err, apiextensions.Resource("customresourcedefinitions"), name)
   164  			err = storageerr.InterpretUpdateError(err, apiextensions.Resource("customresourcedefinitions"), name)
   165  			if _, ok := err.(*apierrors.StatusError); !ok {
   166  				err = apierrors.NewInternalError(err)
   167  			}
   168  			return nil, false, err
   169  		}
   170  
   171  		return out, false, nil
   172  	}
   173  
   174  	return r.Store.Delete(ctx, name, deleteValidation, options)
   175  }
   176  
   177  // NewStatusREST makes a RESTStorage for status that has more limited options.
   178  // It is based on the original REST so that we can share the same underlying store
   179  func NewStatusREST(scheme *runtime.Scheme, rest *REST) *StatusREST {
   180  	statusStore := *rest.Store
   181  	statusStore.CreateStrategy = nil
   182  	statusStore.DeleteStrategy = nil
   183  	statusStrategy := NewStatusStrategy(scheme)
   184  	statusStore.UpdateStrategy = statusStrategy
   185  	statusStore.ResetFieldsStrategy = statusStrategy
   186  	return &StatusREST{store: &statusStore}
   187  }
   188  
   189  type StatusREST struct {
   190  	store *genericregistry.Store
   191  }
   192  
   193  var _ = rest.Patcher(&StatusREST{})
   194  
   195  func (r *StatusREST) New() runtime.Object {
   196  	return &apiextensions.CustomResourceDefinition{}
   197  }
   198  
   199  // Destroy cleans up resources on shutdown.
   200  func (r *StatusREST) Destroy() {
   201  	// Given that underlying store is shared with REST,
   202  	// we don't destroy it here explicitly.
   203  }
   204  
   205  // Get retrieves the object from the storage. It is required to support Patch.
   206  func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
   207  	return r.store.Get(ctx, name, options)
   208  }
   209  
   210  // Update alters the status subset of an object.
   211  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) {
   212  	// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
   213  	// subresources should never allow create on update.
   214  	return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
   215  }
   216  
   217  // GetResetFields implements rest.ResetFieldsStrategy
   218  func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
   219  	return r.store.GetResetFields()
   220  }
   221  

View as plain text