k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/plugin/pkg/admission/network/defaultingressclass/admission.go (about) 1 /* 2 Copyright 2020 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 defaultingressclass 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "sort" 24 25 networkingv1 "k8s.io/api/networking/v1" 26 networkingv1beta1 "k8s.io/api/networking/v1beta1" 27 "k8s.io/apimachinery/pkg/api/errors" 28 "k8s.io/apimachinery/pkg/labels" 29 "k8s.io/apiserver/pkg/admission" 30 genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" 31 "k8s.io/client-go/informers" 32 networkingv1listers "k8s.io/client-go/listers/networking/v1" 33 "k8s.io/klog/v2" 34 "k8s.io/kubernetes/pkg/apis/networking" 35 ) 36 37 const ( 38 // PluginName is the name of this admission controller plugin 39 PluginName = "DefaultIngressClass" 40 ) 41 42 // Register registers a plugin 43 func Register(plugins *admission.Plugins) { 44 plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { 45 plugin := newPlugin() 46 return plugin, nil 47 }) 48 } 49 50 // classDefaulterPlugin holds state for and implements the admission plugin. 51 type classDefaulterPlugin struct { 52 *admission.Handler 53 lister networkingv1listers.IngressClassLister 54 } 55 56 var _ admission.Interface = &classDefaulterPlugin{} 57 var _ admission.MutationInterface = &classDefaulterPlugin{} 58 var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&classDefaulterPlugin{}) 59 60 // newPlugin creates a new admission plugin. 61 func newPlugin() *classDefaulterPlugin { 62 return &classDefaulterPlugin{ 63 Handler: admission.NewHandler(admission.Create), 64 } 65 } 66 67 // SetExternalKubeInformerFactory sets a lister and readyFunc for this 68 // classDefaulterPlugin using the provided SharedInformerFactory. 69 func (a *classDefaulterPlugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { 70 informer := f.Networking().V1().IngressClasses() 71 a.lister = informer.Lister() 72 a.SetReadyFunc(informer.Informer().HasSynced) 73 } 74 75 // ValidateInitialization ensures lister is set. 76 func (a *classDefaulterPlugin) ValidateInitialization() error { 77 if a.lister == nil { 78 return fmt.Errorf("missing lister") 79 } 80 return nil 81 } 82 83 // Admit sets the default value of a Ingress's class if the user did not specify 84 // a class. 85 func (a *classDefaulterPlugin) Admit(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error { 86 if attr.GetResource().GroupResource() != networkingv1.Resource("ingresses") { 87 return nil 88 } 89 90 if len(attr.GetSubresource()) != 0 { 91 return nil 92 } 93 94 ingress, ok := attr.GetObject().(*networking.Ingress) 95 // if we can't convert then we don't handle this object so just return 96 if !ok { 97 klog.V(3).Infof("Expected Ingress resource, got: %v", attr.GetKind()) 98 return errors.NewInternalError(fmt.Errorf("Expected Ingress resource, got: %v", attr.GetKind())) 99 } 100 101 // IngressClassName field has been set, no need to set a default value. 102 if ingress.Spec.IngressClassName != nil { 103 return nil 104 } 105 106 // Ingress class annotation has been set, no need to set a default value. 107 if _, ok := ingress.Annotations[networkingv1beta1.AnnotationIngressClass]; ok { 108 return nil 109 } 110 111 klog.V(4).Infof("No class specified on Ingress %s", ingress.Name) 112 113 defaultClass, err := getDefaultClass(a.lister) 114 if err != nil { 115 return admission.NewForbidden(attr, err) 116 } 117 118 // No default class specified, no need to set a default value. 119 if defaultClass == nil { 120 return nil 121 } 122 123 klog.V(4).Infof("Defaulting class for Ingress %s to %s", ingress.Name, defaultClass.Name) 124 ingress.Spec.IngressClassName = &defaultClass.Name 125 return nil 126 } 127 128 // getDefaultClass returns the default IngressClass from the store, or nil. 129 func getDefaultClass(lister networkingv1listers.IngressClassLister) (*networkingv1.IngressClass, error) { 130 list, err := lister.List(labels.Everything()) 131 if err != nil { 132 return nil, err 133 } 134 135 defaultClasses := []*networkingv1.IngressClass{} 136 for _, class := range list { 137 if class.Annotations[networkingv1.AnnotationIsDefaultIngressClass] == "true" { 138 defaultClasses = append(defaultClasses, class) 139 } 140 } 141 142 if len(defaultClasses) == 0 { 143 return nil, nil 144 } 145 sort.Slice(defaultClasses, func(i, j int) bool { 146 if defaultClasses[i].CreationTimestamp.UnixNano() == defaultClasses[j].CreationTimestamp.UnixNano() { 147 return defaultClasses[i].Name < defaultClasses[j].Name 148 } 149 return defaultClasses[i].CreationTimestamp.UnixNano() > defaultClasses[j].CreationTimestamp.UnixNano() 150 }) 151 if len(defaultClasses) > 1 { 152 klog.V(4).Infof("%d default IngressClasses were found, choosing the newest: %s", len(defaultClasses), defaultClasses[0].Name) 153 } 154 155 return defaultClasses[0], nil 156 }