...
1
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
39 PluginName = "DefaultIngressClass"
40 )
41
42
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
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
61 func newPlugin() *classDefaulterPlugin {
62 return &classDefaulterPlugin{
63 Handler: admission.NewHandler(admission.Create),
64 }
65 }
66
67
68
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
76 func (a *classDefaulterPlugin) ValidateInitialization() error {
77 if a.lister == nil {
78 return fmt.Errorf("missing lister")
79 }
80 return nil
81 }
82
83
84
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
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
102 if ingress.Spec.IngressClassName != nil {
103 return nil
104 }
105
106
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
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
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 }
157
View as plain text