admission.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. Copyright 2016 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 setdefault
  14. import (
  15. "context"
  16. "fmt"
  17. "io"
  18. "k8s.io/klog"
  19. storagev1 "k8s.io/api/storage/v1"
  20. "k8s.io/apimachinery/pkg/api/errors"
  21. "k8s.io/apimachinery/pkg/labels"
  22. "k8s.io/apiserver/pkg/admission"
  23. genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
  24. "k8s.io/client-go/informers"
  25. storagev1listers "k8s.io/client-go/listers/storage/v1"
  26. api "k8s.io/kubernetes/pkg/apis/core"
  27. "k8s.io/kubernetes/pkg/apis/core/helper"
  28. storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
  29. )
  30. const (
  31. // PluginName is the name of this admission controller plugin
  32. PluginName = "DefaultStorageClass"
  33. )
  34. // Register registers a plugin
  35. func Register(plugins *admission.Plugins) {
  36. plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
  37. plugin := newPlugin()
  38. return plugin, nil
  39. })
  40. }
  41. // claimDefaulterPlugin holds state for and implements the admission plugin.
  42. type claimDefaulterPlugin struct {
  43. *admission.Handler
  44. lister storagev1listers.StorageClassLister
  45. }
  46. var _ admission.Interface = &claimDefaulterPlugin{}
  47. var _ admission.MutationInterface = &claimDefaulterPlugin{}
  48. var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&claimDefaulterPlugin{})
  49. // newPlugin creates a new admission plugin.
  50. func newPlugin() *claimDefaulterPlugin {
  51. return &claimDefaulterPlugin{
  52. Handler: admission.NewHandler(admission.Create),
  53. }
  54. }
  55. func (a *claimDefaulterPlugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
  56. informer := f.Storage().V1().StorageClasses()
  57. a.lister = informer.Lister()
  58. a.SetReadyFunc(informer.Informer().HasSynced)
  59. }
  60. // ValidateInitialization ensures lister is set.
  61. func (a *claimDefaulterPlugin) ValidateInitialization() error {
  62. if a.lister == nil {
  63. return fmt.Errorf("missing lister")
  64. }
  65. return nil
  66. }
  67. // Admit sets the default value of a PersistentVolumeClaim's storage class, in case the user did
  68. // not provide a value.
  69. //
  70. // 1. Find available StorageClasses.
  71. // 2. Figure which is the default
  72. // 3. Write to the PVClaim
  73. func (a *claimDefaulterPlugin) Admit(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error {
  74. if attr.GetResource().GroupResource() != api.Resource("persistentvolumeclaims") {
  75. return nil
  76. }
  77. if len(attr.GetSubresource()) != 0 {
  78. return nil
  79. }
  80. pvc, ok := attr.GetObject().(*api.PersistentVolumeClaim)
  81. // if we can't convert then we don't handle this object so just return
  82. if !ok {
  83. return nil
  84. }
  85. if helper.PersistentVolumeClaimHasClass(pvc) {
  86. // The user asked for a class.
  87. return nil
  88. }
  89. klog.V(4).Infof("no storage class for claim %s (generate: %s)", pvc.Name, pvc.GenerateName)
  90. def, err := getDefaultClass(a.lister)
  91. if err != nil {
  92. return admission.NewForbidden(attr, err)
  93. }
  94. if def == nil {
  95. // No default class selected, do nothing about the PVC.
  96. return nil
  97. }
  98. klog.V(4).Infof("defaulting storage class for claim %s (generate: %s) to %s", pvc.Name, pvc.GenerateName, def.Name)
  99. pvc.Spec.StorageClassName = &def.Name
  100. return nil
  101. }
  102. // getDefaultClass returns the default StorageClass from the store, or nil.
  103. func getDefaultClass(lister storagev1listers.StorageClassLister) (*storagev1.StorageClass, error) {
  104. list, err := lister.List(labels.Everything())
  105. if err != nil {
  106. return nil, err
  107. }
  108. defaultClasses := []*storagev1.StorageClass{}
  109. for _, class := range list {
  110. if storageutil.IsDefaultAnnotation(class.ObjectMeta) {
  111. defaultClasses = append(defaultClasses, class)
  112. klog.V(4).Infof("getDefaultClass added: %s", class.Name)
  113. }
  114. }
  115. if len(defaultClasses) == 0 {
  116. return nil, nil
  117. }
  118. if len(defaultClasses) > 1 {
  119. klog.V(4).Infof("getDefaultClass %d defaults found", len(defaultClasses))
  120. return nil, errors.NewInternalError(fmt.Errorf("%d default StorageClasses were found", len(defaultClasses)))
  121. }
  122. return defaultClasses[0], nil
  123. }