...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/network/defaultingressclass/admission.go

Documentation: k8s.io/kubernetes/plugin/pkg/admission/network/defaultingressclass

     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  }
   157  

View as plain text