...

Source file src/k8s.io/kubectl/pkg/cmd/create/create_ingress_test.go

Documentation: k8s.io/kubectl/pkg/cmd/create

     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 create
    18  
    19  import (
    20  	"testing"
    21  
    22  	networkingv1 "k8s.io/api/networking/v1"
    23  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  )
    26  
    27  func TestCreateIngressValidation(t *testing.T) {
    28  	tests := map[string]struct {
    29  		defaultbackend string
    30  		ingressclass   string
    31  		rules          []string
    32  		annotations    []string
    33  		expected       string
    34  	}{
    35  		"no default backend and rule": {
    36  			defaultbackend: "",
    37  			rules:          []string{},
    38  			expected:       "not enough information provided: every ingress has to either specify a default-backend (which catches all traffic) or a list of rules (which catch specific paths)",
    39  		},
    40  		"invalid default backend separator": {
    41  			defaultbackend: "xpto,4444",
    42  			expected:       "default-backend should be in format servicename:serviceport",
    43  		},
    44  		"default backend without port": {
    45  			defaultbackend: "xpto",
    46  			expected:       "default-backend should be in format servicename:serviceport",
    47  		},
    48  		"default backend is ok": {
    49  			defaultbackend: "xpto:4444",
    50  			expected:       "",
    51  		},
    52  		"invalid annotation": {
    53  			defaultbackend: "xpto:4444",
    54  			annotations: []string{
    55  				"key1=value1",
    56  				"key2",
    57  			},
    58  			expected: "annotation key2 is invalid and should be in format key=[value]",
    59  		},
    60  		"valid annotations": {
    61  			defaultbackend: "xpto:4444",
    62  			annotations: []string{
    63  				"key1=value1",
    64  				"key2=",
    65  			},
    66  			expected: "",
    67  		},
    68  		"multiple conformant rules": {
    69  			rules: []string{
    70  				"foo.com/path*=svc:8080",
    71  				"bar.com/admin*=svc2:http",
    72  			},
    73  			expected: "",
    74  		},
    75  		"one invalid and two valid rules": {
    76  			rules: []string{
    77  				"foo.com=svc:redis,tls",
    78  				"foo.com/path/subpath*=othersvc:8080",
    79  				"foo.com/*=svc:8080,tls=secret1",
    80  			},
    81  			expected: "rule foo.com=svc:redis,tls is invalid and should be in format host/path=svcname:svcport[,tls[=secret]]",
    82  		},
    83  		"service without port": {
    84  			rules: []string{
    85  				"foo.com/=svc,tls",
    86  			},
    87  			expected: "rule foo.com/=svc,tls is invalid and should be in format host/path=svcname:svcport[,tls[=secret]]",
    88  		},
    89  		"valid tls rule without secret": {
    90  			rules: []string{
    91  				"foo.com/=svc:http,tls=",
    92  			},
    93  			expected: "",
    94  		},
    95  		"valid tls rule with secret": {
    96  			rules: []string{
    97  				"foo.com/=svc:http,tls=secret123",
    98  			},
    99  			expected: "",
   100  		},
   101  		"valid path with type prefix": {
   102  			rules: []string{
   103  				"foo.com/admin*=svc:8080",
   104  			},
   105  			expected: "",
   106  		},
   107  		"wildcard host": {
   108  			rules: []string{
   109  				"*.foo.com/admin*=svc:8080",
   110  			},
   111  			expected: "",
   112  		},
   113  		"invalid separation between ingress and service": {
   114  			rules: []string{
   115  				"*.foo.com/path,svc:8080",
   116  			},
   117  			expected: "rule *.foo.com/path,svc:8080 is invalid and should be in format host/path=svcname:svcport[,tls[=secret]]",
   118  		},
   119  		"two invalid and one valid rule": {
   120  			rules: []string{
   121  				"foo.com/path/subpath*=svc:redis,tls=blo",
   122  				"foo.com=othersvc:8080",
   123  				"foo.com/admin=svc,tls=secret1",
   124  			},
   125  			expected: "rule foo.com=othersvc:8080 is invalid and should be in format host/path=svcname:svcport[,tls[=secret]]",
   126  		},
   127  		"valid catch all rule": {
   128  			rules: []string{
   129  				"/path/subpath*=svc:redis,tls=blo",
   130  			},
   131  			expected: "",
   132  		},
   133  	}
   134  
   135  	for name, tc := range tests {
   136  		t.Run(name, func(t *testing.T) {
   137  			o := &CreateIngressOptions{
   138  				DefaultBackend: tc.defaultbackend,
   139  				Rules:          tc.rules,
   140  				IngressClass:   tc.ingressclass,
   141  				Annotations:    tc.annotations,
   142  			}
   143  
   144  			err := o.Validate()
   145  			if err != nil && err.Error() != tc.expected {
   146  				t.Errorf("unexpected error: %v", err)
   147  			}
   148  			if tc.expected != "" && err == nil {
   149  				t.Errorf("expected error, got no error")
   150  			}
   151  
   152  		})
   153  	}
   154  }
   155  
   156  func TestCreateIngress(t *testing.T) {
   157  	ingressName := "test-ingress"
   158  	ingressClass := "nginx"
   159  	pathTypeExact := networkingv1.PathTypeExact
   160  	pathTypePrefix := networkingv1.PathTypePrefix
   161  	tests := map[string]struct {
   162  		defaultbackend string
   163  		rules          []string
   164  		ingressclass   string
   165  		annotations    []string
   166  		expected       *networkingv1.Ingress
   167  	}{
   168  		"catch all host and default backend with default TLS returns empty TLS": {
   169  			rules: []string{
   170  				"/=catchall:8080,tls=",
   171  			},
   172  			ingressclass:   ingressClass,
   173  			defaultbackend: "service1:https",
   174  			annotations:    []string{},
   175  			expected: &networkingv1.Ingress{
   176  				TypeMeta: metav1.TypeMeta{
   177  					APIVersion: networkingv1.SchemeGroupVersion.String(),
   178  					Kind:       "Ingress",
   179  				},
   180  				ObjectMeta: metav1.ObjectMeta{
   181  					Name:        ingressName,
   182  					Annotations: map[string]string{},
   183  				},
   184  				Spec: networkingv1.IngressSpec{
   185  					IngressClassName: &ingressClass,
   186  					DefaultBackend: &networkingv1.IngressBackend{
   187  						Service: &networkingv1.IngressServiceBackend{
   188  							Name: "service1",
   189  							Port: networkingv1.ServiceBackendPort{
   190  								Name: "https",
   191  							},
   192  						},
   193  					},
   194  					TLS: []networkingv1.IngressTLS{},
   195  					Rules: []networkingv1.IngressRule{
   196  						{
   197  							Host: "",
   198  							IngressRuleValue: networkingv1.IngressRuleValue{
   199  								HTTP: &networkingv1.HTTPIngressRuleValue{
   200  									Paths: []networkingv1.HTTPIngressPath{
   201  										{
   202  											Path:     "/",
   203  											PathType: &pathTypeExact,
   204  											Backend: networkingv1.IngressBackend{
   205  												Service: &networkingv1.IngressServiceBackend{
   206  													Name: "catchall",
   207  													Port: networkingv1.ServiceBackendPort{
   208  														Number: 8080,
   209  													},
   210  												},
   211  											},
   212  										},
   213  									},
   214  								},
   215  							},
   216  						},
   217  					},
   218  				},
   219  			},
   220  		},
   221  		"catch all with path of type prefix and secret name": {
   222  			rules: []string{
   223  				"/path*=catchall:8080,tls=secret1",
   224  			},
   225  			ingressclass:   ingressClass,
   226  			defaultbackend: "service1:https",
   227  			annotations:    []string{},
   228  			expected: &networkingv1.Ingress{
   229  				TypeMeta: metav1.TypeMeta{
   230  					APIVersion: networkingv1.SchemeGroupVersion.String(),
   231  					Kind:       "Ingress",
   232  				},
   233  				ObjectMeta: metav1.ObjectMeta{
   234  					Name:        ingressName,
   235  					Annotations: map[string]string{},
   236  				},
   237  				Spec: networkingv1.IngressSpec{
   238  					IngressClassName: &ingressClass,
   239  					DefaultBackend: &networkingv1.IngressBackend{
   240  						Service: &networkingv1.IngressServiceBackend{
   241  							Name: "service1",
   242  							Port: networkingv1.ServiceBackendPort{
   243  								Name: "https",
   244  							},
   245  						},
   246  					},
   247  					TLS: []networkingv1.IngressTLS{
   248  						{
   249  							SecretName: "secret1",
   250  						},
   251  					},
   252  					Rules: []networkingv1.IngressRule{
   253  						{
   254  							Host: "",
   255  							IngressRuleValue: networkingv1.IngressRuleValue{
   256  								HTTP: &networkingv1.HTTPIngressRuleValue{
   257  									Paths: []networkingv1.HTTPIngressPath{
   258  										{
   259  											Path:     "/path",
   260  											PathType: &pathTypePrefix,
   261  											Backend: networkingv1.IngressBackend{
   262  												Service: &networkingv1.IngressServiceBackend{
   263  													Name: "catchall",
   264  													Port: networkingv1.ServiceBackendPort{
   265  														Number: 8080,
   266  													},
   267  												},
   268  											},
   269  										},
   270  									},
   271  								},
   272  							},
   273  						},
   274  					},
   275  				},
   276  			},
   277  		},
   278  		"mixed hosts with mixed TLS configuration and a default backend": {
   279  			rules: []string{
   280  				"foo.com/=foo-svc:8080,tls=",
   281  				"foo.com/admin=foo-admin-svc:http,tls=",
   282  				"bar.com/prefix*=bar-svc:8080,tls=bar-secret",
   283  				"bar.com/noprefix=barnp-svc:8443,tls",
   284  				"foobar.com/*=foobar-svc:https",
   285  				"foobar1.com/*=foobar1-svc:https,tls=bar-secret",
   286  			},
   287  			defaultbackend: "service2:8080",
   288  			annotations:    []string{},
   289  			expected: &networkingv1.Ingress{
   290  				TypeMeta: metav1.TypeMeta{
   291  					APIVersion: networkingv1.SchemeGroupVersion.String(),
   292  					Kind:       "Ingress",
   293  				},
   294  				ObjectMeta: metav1.ObjectMeta{
   295  					Name:        ingressName,
   296  					Annotations: map[string]string{},
   297  				},
   298  				Spec: networkingv1.IngressSpec{
   299  					DefaultBackend: &networkingv1.IngressBackend{
   300  						Service: &networkingv1.IngressServiceBackend{
   301  							Name: "service2",
   302  							Port: networkingv1.ServiceBackendPort{
   303  								Number: 8080,
   304  							},
   305  						},
   306  					},
   307  					TLS: []networkingv1.IngressTLS{
   308  						{
   309  							Hosts: []string{
   310  								"foo.com",
   311  							},
   312  						},
   313  						{
   314  							Hosts: []string{
   315  								"bar.com",
   316  								"foobar1.com",
   317  							},
   318  							SecretName: "bar-secret",
   319  						},
   320  					},
   321  					Rules: []networkingv1.IngressRule{
   322  						{
   323  							Host: "foo.com",
   324  							IngressRuleValue: networkingv1.IngressRuleValue{
   325  								HTTP: &networkingv1.HTTPIngressRuleValue{
   326  									Paths: []networkingv1.HTTPIngressPath{
   327  										{
   328  											Path:     "/",
   329  											PathType: &pathTypeExact,
   330  											Backend: networkingv1.IngressBackend{
   331  												Service: &networkingv1.IngressServiceBackend{
   332  													Name: "foo-svc",
   333  													Port: networkingv1.ServiceBackendPort{
   334  														Number: 8080,
   335  													},
   336  												},
   337  											},
   338  										},
   339  										{
   340  											Path:     "/admin",
   341  											PathType: &pathTypeExact,
   342  											Backend: networkingv1.IngressBackend{
   343  												Service: &networkingv1.IngressServiceBackend{
   344  													Name: "foo-admin-svc",
   345  													Port: networkingv1.ServiceBackendPort{
   346  														Name: "http",
   347  													},
   348  												},
   349  											},
   350  										},
   351  									},
   352  								},
   353  							},
   354  						},
   355  						{
   356  							Host: "bar.com",
   357  							IngressRuleValue: networkingv1.IngressRuleValue{
   358  								HTTP: &networkingv1.HTTPIngressRuleValue{
   359  									Paths: []networkingv1.HTTPIngressPath{
   360  										{
   361  											Path:     "/prefix",
   362  											PathType: &pathTypePrefix,
   363  											Backend: networkingv1.IngressBackend{
   364  												Service: &networkingv1.IngressServiceBackend{
   365  													Name: "bar-svc",
   366  													Port: networkingv1.ServiceBackendPort{
   367  														Number: 8080,
   368  													},
   369  												},
   370  											},
   371  										},
   372  										{
   373  											Path:     "/noprefix",
   374  											PathType: &pathTypeExact,
   375  											Backend: networkingv1.IngressBackend{
   376  												Service: &networkingv1.IngressServiceBackend{
   377  													Name: "barnp-svc",
   378  													Port: networkingv1.ServiceBackendPort{
   379  														Number: 8443,
   380  													},
   381  												},
   382  											},
   383  										},
   384  									},
   385  								},
   386  							},
   387  						},
   388  						{
   389  							Host: "foobar.com",
   390  							IngressRuleValue: networkingv1.IngressRuleValue{
   391  								HTTP: &networkingv1.HTTPIngressRuleValue{
   392  									Paths: []networkingv1.HTTPIngressPath{
   393  										{
   394  											Path:     "/",
   395  											PathType: &pathTypePrefix,
   396  											Backend: networkingv1.IngressBackend{
   397  												Service: &networkingv1.IngressServiceBackend{
   398  													Name: "foobar-svc",
   399  													Port: networkingv1.ServiceBackendPort{
   400  														Name: "https",
   401  													},
   402  												},
   403  											},
   404  										},
   405  									},
   406  								},
   407  							},
   408  						},
   409  						{
   410  							Host: "foobar1.com",
   411  							IngressRuleValue: networkingv1.IngressRuleValue{
   412  								HTTP: &networkingv1.HTTPIngressRuleValue{
   413  									Paths: []networkingv1.HTTPIngressPath{
   414  										{
   415  											Path:     "/",
   416  											PathType: &pathTypePrefix,
   417  											Backend: networkingv1.IngressBackend{
   418  												Service: &networkingv1.IngressServiceBackend{
   419  													Name: "foobar1-svc",
   420  													Port: networkingv1.ServiceBackendPort{
   421  														Name: "https",
   422  													},
   423  												},
   424  											},
   425  										},
   426  									},
   427  								},
   428  							},
   429  						},
   430  					},
   431  				},
   432  			},
   433  		},
   434  		"simple ingress with annotation": {
   435  			rules: []string{
   436  				"foo.com/=svc:8080,tls=secret1",
   437  				"foo.com/subpath*=othersvc:8080,tls=secret1",
   438  			},
   439  			annotations: []string{
   440  				"ingress.kubernetes.io/annotation1=bla",
   441  				"ingress.kubernetes.io/annotation2=blo",
   442  				"ingress.kubernetes.io/annotation3=ble",
   443  			},
   444  			expected: &networkingv1.Ingress{
   445  				TypeMeta: metav1.TypeMeta{
   446  					APIVersion: networkingv1.SchemeGroupVersion.String(),
   447  					Kind:       "Ingress",
   448  				},
   449  				ObjectMeta: metav1.ObjectMeta{
   450  					Name: ingressName,
   451  					Annotations: map[string]string{
   452  						"ingress.kubernetes.io/annotation1": "bla",
   453  						"ingress.kubernetes.io/annotation3": "ble",
   454  						"ingress.kubernetes.io/annotation2": "blo",
   455  					},
   456  				},
   457  				Spec: networkingv1.IngressSpec{
   458  					TLS: []networkingv1.IngressTLS{
   459  						{
   460  							Hosts: []string{
   461  								"foo.com",
   462  							},
   463  							SecretName: "secret1",
   464  						},
   465  					},
   466  					Rules: []networkingv1.IngressRule{
   467  						{
   468  							Host: "foo.com",
   469  							IngressRuleValue: networkingv1.IngressRuleValue{
   470  								HTTP: &networkingv1.HTTPIngressRuleValue{
   471  									Paths: []networkingv1.HTTPIngressPath{
   472  										{
   473  											Path:     "/",
   474  											PathType: &pathTypeExact,
   475  											Backend: networkingv1.IngressBackend{
   476  												Service: &networkingv1.IngressServiceBackend{
   477  													Name: "svc",
   478  													Port: networkingv1.ServiceBackendPort{
   479  														Number: 8080,
   480  													},
   481  												},
   482  											},
   483  										},
   484  										{
   485  											Path:     "/subpath",
   486  											PathType: &pathTypePrefix,
   487  											Backend: networkingv1.IngressBackend{
   488  												Service: &networkingv1.IngressServiceBackend{
   489  													Name: "othersvc",
   490  													Port: networkingv1.ServiceBackendPort{
   491  														Number: 8080,
   492  													},
   493  												},
   494  											},
   495  										},
   496  									},
   497  								},
   498  							},
   499  						},
   500  					},
   501  				},
   502  			},
   503  		},
   504  	}
   505  
   506  	for name, tc := range tests {
   507  		t.Run(name, func(t *testing.T) {
   508  			o := &CreateIngressOptions{
   509  				Name:           ingressName,
   510  				IngressClass:   tc.ingressclass,
   511  				Annotations:    tc.annotations,
   512  				DefaultBackend: tc.defaultbackend,
   513  				Rules:          tc.rules,
   514  			}
   515  			ingress := o.createIngress()
   516  			if !apiequality.Semantic.DeepEqual(ingress, tc.expected) {
   517  				t.Errorf("expected:\n%#v\ngot:\n%#v", tc.expected, ingress)
   518  			}
   519  		})
   520  	}
   521  }
   522  

View as plain text