...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/network/defaultingressclass/admission_test.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  	"reflect"
    22  	"testing"
    23  	"time"
    24  
    25  	networkingv1 "k8s.io/api/networking/v1"
    26  	networkingv1beta1 "k8s.io/api/networking/v1beta1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apiserver/pkg/admission"
    29  	admissiontesting "k8s.io/apiserver/pkg/admission/testing"
    30  	"k8s.io/client-go/informers"
    31  	api "k8s.io/kubernetes/pkg/apis/core"
    32  	"k8s.io/kubernetes/pkg/apis/networking"
    33  	"k8s.io/kubernetes/pkg/controller"
    34  	utilpointer "k8s.io/utils/pointer"
    35  )
    36  
    37  func TestAdmission(t *testing.T) {
    38  	defaultClass1 := &networkingv1.IngressClass{
    39  		TypeMeta: metav1.TypeMeta{
    40  			Kind: "IngressClass",
    41  		},
    42  		ObjectMeta: metav1.ObjectMeta{
    43  			Name: "default1",
    44  			Annotations: map[string]string{
    45  				networkingv1.AnnotationIsDefaultIngressClass: "true",
    46  			},
    47  		},
    48  	}
    49  	defaultClass2 := &networkingv1.IngressClass{
    50  		ObjectMeta: metav1.ObjectMeta{
    51  			Name: "default2",
    52  			Annotations: map[string]string{
    53  				networkingv1.AnnotationIsDefaultIngressClass: "true",
    54  			},
    55  		},
    56  	}
    57  	// Class that has explicit default = false
    58  	classWithFalseDefault := &networkingv1.IngressClass{
    59  		TypeMeta: metav1.TypeMeta{
    60  			Kind: "IngressClass",
    61  		},
    62  		ObjectMeta: metav1.ObjectMeta{
    63  			Name: "nondefault1",
    64  			Annotations: map[string]string{
    65  				networkingv1.AnnotationIsDefaultIngressClass: "false",
    66  			},
    67  		},
    68  	}
    69  	// Class with missing default annotation (=non-default)
    70  	classWithNoDefault := &networkingv1.IngressClass{
    71  		TypeMeta: metav1.TypeMeta{
    72  			Kind: "IngressClass",
    73  		},
    74  		ObjectMeta: metav1.ObjectMeta{
    75  			Name: "nondefault2",
    76  		},
    77  	}
    78  	// Class with empty default annotation (=non-default)
    79  	classWithEmptyDefault := &networkingv1.IngressClass{
    80  		TypeMeta: metav1.TypeMeta{
    81  			Kind: "IngressClass",
    82  		},
    83  		ObjectMeta: metav1.ObjectMeta{
    84  			Name: "nondefault2",
    85  			Annotations: map[string]string{
    86  				networkingv1.AnnotationIsDefaultIngressClass: "",
    87  			},
    88  		},
    89  	}
    90  
    91  	defaultClassWithCreateTime1 := &networkingv1.IngressClass{
    92  		TypeMeta: metav1.TypeMeta{
    93  			Kind: "IngressClass",
    94  		},
    95  		ObjectMeta: metav1.ObjectMeta{
    96  			Name:              "default1",
    97  			CreationTimestamp: metav1.NewTime(time.Date(2022, time.Month(1), 1, 0, 0, 0, 1, time.UTC)),
    98  			Annotations: map[string]string{
    99  				networkingv1.AnnotationIsDefaultIngressClass: "true",
   100  			},
   101  		},
   102  	}
   103  	defaultClassWithCreateTime2 := &networkingv1.IngressClass{
   104  		TypeMeta: metav1.TypeMeta{
   105  			Kind: "IngressClass",
   106  		},
   107  		ObjectMeta: metav1.ObjectMeta{
   108  			Name:              "default2",
   109  			CreationTimestamp: metav1.NewTime(time.Date(2022, time.Month(1), 1, 0, 0, 0, 0, time.UTC)),
   110  			Annotations: map[string]string{
   111  				networkingv1.AnnotationIsDefaultIngressClass: "true",
   112  			},
   113  		},
   114  	}
   115  
   116  	testCases := []struct {
   117  		name            string
   118  		classes         []*networkingv1.IngressClass
   119  		classField      *string
   120  		classAnnotation *string
   121  		expectedClass   *string
   122  		expectedError   error
   123  	}{
   124  		{
   125  			name:            "no default, no modification of Ingress",
   126  			classes:         []*networkingv1.IngressClass{classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   127  			classField:      nil,
   128  			classAnnotation: nil,
   129  			expectedClass:   nil,
   130  			expectedError:   nil,
   131  		},
   132  		{
   133  			name:            "one default, modify Ingress with class=nil",
   134  			classes:         []*networkingv1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   135  			classField:      nil,
   136  			classAnnotation: nil,
   137  			expectedClass:   utilpointer.String(defaultClass1.Name),
   138  			expectedError:   nil,
   139  		},
   140  		{
   141  			name:            "one default, no modification of Ingress with class field=''",
   142  			classes:         []*networkingv1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   143  			classField:      utilpointer.String(""),
   144  			classAnnotation: nil,
   145  			expectedClass:   utilpointer.String(""),
   146  			expectedError:   nil,
   147  		},
   148  		{
   149  			name:            "one default, no modification of Ingress with class field='foo'",
   150  			classes:         []*networkingv1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   151  			classField:      utilpointer.String("foo"),
   152  			classAnnotation: nil,
   153  			expectedClass:   utilpointer.String("foo"),
   154  			expectedError:   nil,
   155  		},
   156  		{
   157  			name:            "one default, no modification of Ingress with class annotation='foo'",
   158  			classes:         []*networkingv1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   159  			classField:      nil,
   160  			classAnnotation: utilpointer.String("foo"),
   161  			expectedClass:   nil,
   162  			expectedError:   nil,
   163  		},
   164  		{
   165  			name:            "two defaults with the same creation time, choose the one with the lower name",
   166  			classes:         []*networkingv1.IngressClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   167  			classField:      nil,
   168  			classAnnotation: nil,
   169  			expectedClass:   utilpointer.String(defaultClass1.Name),
   170  			expectedError:   nil,
   171  		},
   172  		{
   173  			name:            "two defaults, no modification with Ingress with class field=''",
   174  			classes:         []*networkingv1.IngressClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   175  			classField:      utilpointer.String(""),
   176  			classAnnotation: nil,
   177  			expectedClass:   utilpointer.String(""),
   178  			expectedError:   nil,
   179  		},
   180  		{
   181  			name:            "two defaults, choose the one with the newer creation time",
   182  			classes:         []*networkingv1.IngressClass{defaultClassWithCreateTime1, defaultClassWithCreateTime2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   183  			classField:      nil,
   184  			classAnnotation: nil,
   185  			expectedClass:   utilpointer.String(defaultClassWithCreateTime1.Name),
   186  			expectedError:   nil,
   187  		},
   188  	}
   189  
   190  	for _, testCase := range testCases {
   191  		t.Run(testCase.name, func(t *testing.T) {
   192  			ctrl := newPlugin()
   193  			informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   194  			ctrl.SetExternalKubeInformerFactory(informerFactory)
   195  			for _, c := range testCase.classes {
   196  				informerFactory.Networking().V1().IngressClasses().Informer().GetStore().Add(c)
   197  			}
   198  
   199  			ingress := &networking.Ingress{ObjectMeta: metav1.ObjectMeta{Name: "testing", Namespace: "testing"}}
   200  			if testCase.classField != nil {
   201  				ingress.Spec.IngressClassName = testCase.classField
   202  			}
   203  			if testCase.classAnnotation != nil {
   204  				ingress.Annotations = map[string]string{networkingv1beta1.AnnotationIngressClass: *testCase.classAnnotation}
   205  			}
   206  
   207  			attrs := admission.NewAttributesRecord(
   208  				ingress, // new object
   209  				nil,     // old object
   210  				api.Kind("Ingress").WithVersion("version"),
   211  				ingress.Namespace,
   212  				ingress.Name,
   213  				networkingv1.Resource("ingresses").WithVersion("version"),
   214  				"", // subresource
   215  				admission.Create,
   216  				&metav1.CreateOptions{},
   217  				false, // dryRun
   218  				nil,   // userInfo
   219  			)
   220  
   221  			err := admissiontesting.WithReinvocationTesting(t, ctrl).Admit(context.TODO(), attrs, nil)
   222  			if !reflect.DeepEqual(err, testCase.expectedError) {
   223  				t.Errorf("Expected error: %v, got %v", testCase.expectedError, err)
   224  			}
   225  			if !reflect.DeepEqual(testCase.expectedClass, ingress.Spec.IngressClassName) {
   226  				t.Errorf("Expected class name %+v, got %+v", *testCase.expectedClass, ingress.Spec.IngressClassName)
   227  			}
   228  		})
   229  	}
   230  }
   231  

View as plain text