admission.go 4.3 KB

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