etcd.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package etcd
  14. import (
  15. "fmt"
  16. "k8s.io/kubernetes/pkg/api"
  17. apierrors "k8s.io/kubernetes/pkg/api/errors"
  18. storageerr "k8s.io/kubernetes/pkg/api/errors/storage"
  19. "k8s.io/kubernetes/pkg/api/rest"
  20. "k8s.io/kubernetes/pkg/api/unversioned"
  21. "k8s.io/kubernetes/pkg/registry/cachesize"
  22. "k8s.io/kubernetes/pkg/registry/generic"
  23. "k8s.io/kubernetes/pkg/registry/generic/registry"
  24. "k8s.io/kubernetes/pkg/registry/namespace"
  25. "k8s.io/kubernetes/pkg/runtime"
  26. "k8s.io/kubernetes/pkg/storage"
  27. )
  28. // rest implements a RESTStorage for namespaces against etcd
  29. type REST struct {
  30. *registry.Store
  31. status *registry.Store
  32. }
  33. // StatusREST implements the REST endpoint for changing the status of a namespace.
  34. type StatusREST struct {
  35. store *registry.Store
  36. }
  37. // FinalizeREST implements the REST endpoint for finalizing a namespace.
  38. type FinalizeREST struct {
  39. store *registry.Store
  40. }
  41. // NewREST returns a RESTStorage object that will work against namespaces.
  42. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST, *FinalizeREST) {
  43. prefix := "/" + opts.ResourcePrefix
  44. newListFunc := func() runtime.Object { return &api.NamespaceList{} }
  45. storageInterface, _ := opts.Decorator(
  46. opts.StorageConfig,
  47. cachesize.GetWatchCacheSizeByResource(cachesize.Namespaces),
  48. &api.Namespace{},
  49. prefix,
  50. namespace.Strategy,
  51. newListFunc,
  52. storage.NoTriggerPublisher,
  53. )
  54. store := &registry.Store{
  55. NewFunc: func() runtime.Object { return &api.Namespace{} },
  56. NewListFunc: newListFunc,
  57. KeyRootFunc: func(ctx api.Context) string {
  58. return prefix
  59. },
  60. KeyFunc: func(ctx api.Context, name string) (string, error) {
  61. return registry.NoNamespaceKeyFunc(ctx, prefix, name)
  62. },
  63. ObjectNameFunc: func(obj runtime.Object) (string, error) {
  64. return obj.(*api.Namespace).Name, nil
  65. },
  66. PredicateFunc: namespace.MatchNamespace,
  67. QualifiedResource: api.Resource("namespaces"),
  68. DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
  69. CreateStrategy: namespace.Strategy,
  70. UpdateStrategy: namespace.Strategy,
  71. DeleteStrategy: namespace.Strategy,
  72. ReturnDeletedObject: true,
  73. Storage: storageInterface,
  74. }
  75. statusStore := *store
  76. statusStore.UpdateStrategy = namespace.StatusStrategy
  77. finalizeStore := *store
  78. finalizeStore.UpdateStrategy = namespace.FinalizeStrategy
  79. return &REST{Store: store, status: &statusStore}, &StatusREST{store: &statusStore}, &FinalizeREST{store: &finalizeStore}
  80. }
  81. // Delete enforces life-cycle rules for namespace termination
  82. func (r *REST) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) {
  83. nsObj, err := r.Get(ctx, name)
  84. if err != nil {
  85. return nil, err
  86. }
  87. namespace := nsObj.(*api.Namespace)
  88. // Ensure we have a UID precondition
  89. if options == nil {
  90. options = api.NewDeleteOptions(0)
  91. }
  92. if options.Preconditions == nil {
  93. options.Preconditions = &api.Preconditions{}
  94. }
  95. if options.Preconditions.UID == nil {
  96. options.Preconditions.UID = &namespace.UID
  97. } else if *options.Preconditions.UID != namespace.UID {
  98. err = apierrors.NewConflict(
  99. api.Resource("namespaces"),
  100. name,
  101. fmt.Errorf("Precondition failed: UID in precondition: %v, UID in object meta: %v", *options.Preconditions.UID, namespace.UID),
  102. )
  103. return nil, err
  104. }
  105. // upon first request to delete, we switch the phase to start namespace termination
  106. // TODO: enhance graceful deletion's calls to DeleteStrategy to allow phase change and finalizer patterns
  107. if namespace.DeletionTimestamp.IsZero() {
  108. key, err := r.Store.KeyFunc(ctx, name)
  109. if err != nil {
  110. return nil, err
  111. }
  112. preconditions := storage.Preconditions{UID: options.Preconditions.UID}
  113. out := r.Store.NewFunc()
  114. err = r.Store.Storage.GuaranteedUpdate(
  115. ctx, key, out, false, &preconditions,
  116. storage.SimpleUpdate(func(existing runtime.Object) (runtime.Object, error) {
  117. existingNamespace, ok := existing.(*api.Namespace)
  118. if !ok {
  119. // wrong type
  120. return nil, fmt.Errorf("expected *api.Namespace, got %v", existing)
  121. }
  122. // Set the deletion timestamp if needed
  123. if existingNamespace.DeletionTimestamp.IsZero() {
  124. now := unversioned.Now()
  125. existingNamespace.DeletionTimestamp = &now
  126. }
  127. // Set the namespace phase to terminating, if needed
  128. if existingNamespace.Status.Phase != api.NamespaceTerminating {
  129. existingNamespace.Status.Phase = api.NamespaceTerminating
  130. }
  131. return existingNamespace, nil
  132. }),
  133. )
  134. if err != nil {
  135. err = storageerr.InterpretGetError(err, api.Resource("namespaces"), name)
  136. err = storageerr.InterpretUpdateError(err, api.Resource("namespaces"), name)
  137. if _, ok := err.(*apierrors.StatusError); !ok {
  138. err = apierrors.NewInternalError(err)
  139. }
  140. return nil, err
  141. }
  142. return out, nil
  143. }
  144. // prior to final deletion, we must ensure that finalizers is empty
  145. if len(namespace.Spec.Finalizers) != 0 {
  146. err = apierrors.NewConflict(api.Resource("namespaces"), namespace.Name, fmt.Errorf("The system is ensuring all content is removed from this namespace. Upon completion, this namespace will automatically be purged by the system."))
  147. return nil, err
  148. }
  149. return r.Store.Delete(ctx, name, options)
  150. }
  151. func (r *StatusREST) New() runtime.Object {
  152. return r.store.New()
  153. }
  154. // Get retrieves the object from the storage. It is required to support Patch.
  155. func (r *StatusREST) Get(ctx api.Context, name string) (runtime.Object, error) {
  156. return r.store.Get(ctx, name)
  157. }
  158. // Update alters the status subset of an object.
  159. func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
  160. return r.store.Update(ctx, name, objInfo)
  161. }
  162. func (r *FinalizeREST) New() runtime.Object {
  163. return r.store.New()
  164. }
  165. // Update alters the status finalizers subset of an object.
  166. func (r *FinalizeREST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
  167. return r.store.Update(ctx, name, objInfo)
  168. }