...

Source file src/sigs.k8s.io/gateway-api/pkg/test/cel/gateway_test.go

Documentation: sigs.k8s.io/gateway-api/pkg/test/cel

     1  /*
     2  Copyright 2023 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 main
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
    27  
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  )
    30  
    31  func TestValidateGateway(t *testing.T) {
    32  	ctx := context.Background()
    33  	baseGateway := gatewayv1.Gateway{
    34  		ObjectMeta: metav1.ObjectMeta{
    35  			Name:      "foo",
    36  			Namespace: metav1.NamespaceDefault,
    37  		},
    38  		Spec: gatewayv1.GatewaySpec{
    39  			GatewayClassName: "foo",
    40  			Listeners: []gatewayv1.Listener{
    41  				{
    42  					Name:     gatewayv1.SectionName("http"),
    43  					Protocol: gatewayv1.HTTPProtocolType,
    44  					Port:     gatewayv1.PortNumber(80),
    45  				},
    46  			},
    47  		},
    48  	}
    49  
    50  	testCases := []struct {
    51  		desc         string
    52  		mutate       func(gw *gatewayv1.Gateway)
    53  		mutateStatus func(gw *gatewayv1.Gateway)
    54  		wantErrors   []string
    55  	}{
    56  		{
    57  			desc: "tls config present with http protocol",
    58  			mutate: func(gw *gatewayv1.Gateway) {
    59  				gw.Spec.Listeners = []gatewayv1.Listener{
    60  					{
    61  						Name:     gatewayv1.SectionName("http"),
    62  						Protocol: gatewayv1.HTTPProtocolType,
    63  						Port:     gatewayv1.PortNumber(8080),
    64  						TLS:      &gatewayv1.GatewayTLSConfig{},
    65  					},
    66  				}
    67  			},
    68  			wantErrors: []string{"tls must not be specified for protocols ['HTTP', 'TCP', 'UDP']"},
    69  		},
    70  		{
    71  			desc: "tls config present with tcp protocol",
    72  			mutate: func(gw *gatewayv1.Gateway) {
    73  				gw.Spec.Listeners = []gatewayv1.Listener{
    74  					{
    75  						Name:     gatewayv1.SectionName("tcp"),
    76  						Protocol: gatewayv1.TCPProtocolType,
    77  						Port:     gatewayv1.PortNumber(8080),
    78  						TLS:      &gatewayv1.GatewayTLSConfig{},
    79  					},
    80  				}
    81  			},
    82  			wantErrors: []string{"tls must not be specified for protocols ['HTTP', 'TCP', 'UDP']"},
    83  		},
    84  		{
    85  			desc: "tls config not set with https protocol",
    86  			mutate: func(gw *gatewayv1.Gateway) {
    87  				gw.Spec.Listeners = []gatewayv1.Listener{
    88  					{
    89  						Name:     gatewayv1.SectionName("https"),
    90  						Protocol: gatewayv1.HTTPSProtocolType,
    91  						Port:     gatewayv1.PortNumber(8443),
    92  					},
    93  				}
    94  			},
    95  			wantErrors: []string{"tls must be specified for protocols ['HTTPS', 'TLS']"},
    96  		},
    97  		{
    98  			desc: "tls config not set with tls protocol",
    99  			mutate: func(gw *gatewayv1.Gateway) {
   100  				gw.Spec.Listeners = []gatewayv1.Listener{
   101  					{
   102  						Name:     gatewayv1.SectionName("tls"),
   103  						Protocol: gatewayv1.TLSProtocolType,
   104  						Port:     gatewayv1.PortNumber(8443),
   105  					},
   106  				}
   107  			},
   108  			wantErrors: []string{"tls must be specified for protocols ['HTTPS', 'TLS']"},
   109  		},
   110  		{
   111  			desc: "tls config not set with http protocol",
   112  			mutate: func(gw *gatewayv1.Gateway) {
   113  				gw.Spec.Listeners = []gatewayv1.Listener{
   114  					{
   115  						Name:     gatewayv1.SectionName("http"),
   116  						Protocol: gatewayv1.HTTPProtocolType,
   117  						Port:     gatewayv1.PortNumber(8080),
   118  					},
   119  				}
   120  			},
   121  		},
   122  		{
   123  			desc: "tls config not set with tcp protocol",
   124  			mutate: func(gw *gatewayv1.Gateway) {
   125  				gw.Spec.Listeners = []gatewayv1.Listener{
   126  					{
   127  						Name:     gatewayv1.SectionName("tcp"),
   128  						Protocol: gatewayv1.TCPProtocolType,
   129  						Port:     gatewayv1.PortNumber(8080),
   130  					},
   131  				}
   132  			},
   133  		},
   134  		{
   135  			desc: "tls config not set with udp protocol",
   136  			mutate: func(gw *gatewayv1.Gateway) {
   137  				gw.Spec.Listeners = []gatewayv1.Listener{
   138  					{
   139  						Name:     gatewayv1.SectionName("udp"),
   140  						Protocol: gatewayv1.UDPProtocolType,
   141  						Port:     gatewayv1.PortNumber(8080),
   142  					},
   143  				}
   144  			},
   145  		},
   146  		{
   147  			desc: "hostname present with tcp protocol",
   148  			mutate: func(gw *gatewayv1.Gateway) {
   149  				hostname := gatewayv1.Hostname("foo")
   150  				gw.Spec.Listeners = []gatewayv1.Listener{
   151  					{
   152  						Name:     gatewayv1.SectionName("tcp"),
   153  						Protocol: gatewayv1.TCPProtocolType,
   154  						Port:     gatewayv1.PortNumber(8080),
   155  						Hostname: &hostname,
   156  					},
   157  				}
   158  			},
   159  			wantErrors: []string{"hostname must not be specified for protocols ['TCP', 'UDP']"},
   160  		},
   161  		{
   162  			desc: "hostname present with udp protocol",
   163  			mutate: func(gw *gatewayv1.Gateway) {
   164  				hostname := gatewayv1.Hostname("foo")
   165  				gw.Spec.Listeners = []gatewayv1.Listener{
   166  					{
   167  						Name:     gatewayv1.SectionName("udp"),
   168  						Protocol: gatewayv1.UDPProtocolType,
   169  						Port:     gatewayv1.PortNumber(8080),
   170  						Hostname: &hostname,
   171  					},
   172  				}
   173  			},
   174  			wantErrors: []string{"hostname must not be specified for protocols ['TCP', 'UDP']"},
   175  		},
   176  		{
   177  			desc: "certificateRefs not set with https protocol and TLS terminate mode",
   178  			mutate: func(gw *gatewayv1.Gateway) {
   179  				tlsMode := gatewayv1.TLSModeType("Terminate")
   180  				gw.Spec.Listeners = []gatewayv1.Listener{
   181  					{
   182  						Name:     gatewayv1.SectionName("https"),
   183  						Protocol: gatewayv1.HTTPSProtocolType,
   184  						Port:     gatewayv1.PortNumber(8443),
   185  						TLS: &gatewayv1.GatewayTLSConfig{
   186  							Mode: &tlsMode,
   187  						},
   188  					},
   189  				}
   190  			},
   191  			wantErrors: []string{"certificateRefs must be specified when TLSModeType is Terminate"},
   192  		},
   193  		{
   194  			desc: "certificateRefs not set with tls protocol and TLS terminate mode",
   195  			mutate: func(gw *gatewayv1.Gateway) {
   196  				tlsMode := gatewayv1.TLSModeType("Terminate")
   197  				gw.Spec.Listeners = []gatewayv1.Listener{
   198  					{
   199  						Name:     gatewayv1.SectionName("tls"),
   200  						Protocol: gatewayv1.TLSProtocolType,
   201  						Port:     gatewayv1.PortNumber(8443),
   202  						TLS: &gatewayv1.GatewayTLSConfig{
   203  							Mode: &tlsMode,
   204  						},
   205  					},
   206  				}
   207  			},
   208  			wantErrors: []string{"certificateRefs must be specified when TLSModeType is Terminate"},
   209  		},
   210  		{
   211  			desc: "certificateRefs set with tls protocol and TLS terminate mode",
   212  			mutate: func(gw *gatewayv1.Gateway) {
   213  				tlsMode := gatewayv1.TLSModeType("Terminate")
   214  				gw.Spec.Listeners = []gatewayv1.Listener{
   215  					{
   216  						Name:     gatewayv1.SectionName("tls"),
   217  						Protocol: gatewayv1.TLSProtocolType,
   218  						Port:     gatewayv1.PortNumber(8443),
   219  						TLS: &gatewayv1.GatewayTLSConfig{
   220  							Mode: &tlsMode,
   221  							CertificateRefs: []gatewayv1.SecretObjectReference{
   222  								{Name: gatewayv1.ObjectName("foo")},
   223  							},
   224  						},
   225  					},
   226  				}
   227  			},
   228  		},
   229  		{
   230  			desc: "names are not unique within the Gateway",
   231  			mutate: func(gw *gatewayv1.Gateway) {
   232  				gw.Spec.Listeners = []gatewayv1.Listener{
   233  					{
   234  						Name:     gatewayv1.SectionName("http"),
   235  						Protocol: gatewayv1.HTTPProtocolType,
   236  						Port:     gatewayv1.PortNumber(80),
   237  					},
   238  					{
   239  						Name:     gatewayv1.SectionName("http"),
   240  						Protocol: gatewayv1.HTTPProtocolType,
   241  						Port:     gatewayv1.PortNumber(8000),
   242  					},
   243  					{
   244  						Name:     gatewayv1.SectionName("http"),
   245  						Protocol: gatewayv1.HTTPProtocolType,
   246  						Port:     gatewayv1.PortNumber(8080),
   247  					},
   248  				}
   249  			},
   250  			wantErrors: []string{"Listener name must be unique within the Gateway"},
   251  		},
   252  		{
   253  			desc: "names are unique within the Gateway",
   254  			mutate: func(gw *gatewayv1.Gateway) {
   255  				gw.Spec.Listeners = []gatewayv1.Listener{
   256  					{
   257  						Name:     gatewayv1.SectionName("http-1"),
   258  						Protocol: gatewayv1.HTTPProtocolType,
   259  						Port:     gatewayv1.PortNumber(80),
   260  					},
   261  					{
   262  						Name:     gatewayv1.SectionName("http-2"),
   263  						Protocol: gatewayv1.HTTPProtocolType,
   264  						Port:     gatewayv1.PortNumber(8000),
   265  					},
   266  					{
   267  						Name:     gatewayv1.SectionName("http-3"),
   268  						Protocol: gatewayv1.HTTPProtocolType,
   269  						Port:     gatewayv1.PortNumber(8080),
   270  					},
   271  				}
   272  			},
   273  		},
   274  		{
   275  			desc: "combination of port, protocol, and hostname are not unique for each listener",
   276  			mutate: func(gw *gatewayv1.Gateway) {
   277  				hostnameFoo := gatewayv1.Hostname("foo.com")
   278  				gw.Spec.Listeners = []gatewayv1.Listener{
   279  					{
   280  						Name:     gatewayv1.SectionName("foo"),
   281  						Protocol: gatewayv1.HTTPProtocolType,
   282  						Port:     gatewayv1.PortNumber(80),
   283  						Hostname: &hostnameFoo,
   284  					},
   285  					{
   286  						Name:     gatewayv1.SectionName("bar"),
   287  						Protocol: gatewayv1.HTTPProtocolType,
   288  						Port:     gatewayv1.PortNumber(80),
   289  						Hostname: &hostnameFoo,
   290  					},
   291  				}
   292  			},
   293  			wantErrors: []string{"Combination of port, protocol and hostname must be unique for each listener"},
   294  		},
   295  		{
   296  			desc: "combination of port and protocol are not unique for each listener when hostnames not set",
   297  			mutate: func(gw *gatewayv1.Gateway) {
   298  				gw.Spec.Listeners = []gatewayv1.Listener{
   299  					{
   300  						Name:     gatewayv1.SectionName("foo"),
   301  						Protocol: gatewayv1.HTTPProtocolType,
   302  						Port:     gatewayv1.PortNumber(80),
   303  					},
   304  					{
   305  						Name:     gatewayv1.SectionName("bar"),
   306  						Protocol: gatewayv1.HTTPProtocolType,
   307  						Port:     gatewayv1.PortNumber(80),
   308  					},
   309  				}
   310  			},
   311  			wantErrors: []string{"Combination of port, protocol and hostname must be unique for each listener"},
   312  		},
   313  		{
   314  			desc: "port is unique when protocol and hostname are the same",
   315  			mutate: func(gw *gatewayv1.Gateway) {
   316  				hostnameFoo := gatewayv1.Hostname("foo.com")
   317  				gw.Spec.Listeners = []gatewayv1.Listener{
   318  					{
   319  						Name:     gatewayv1.SectionName("foo"),
   320  						Protocol: gatewayv1.HTTPProtocolType,
   321  						Port:     gatewayv1.PortNumber(80),
   322  						Hostname: &hostnameFoo,
   323  					},
   324  					{
   325  						Name:     gatewayv1.SectionName("bar"),
   326  						Protocol: gatewayv1.HTTPProtocolType,
   327  						Port:     gatewayv1.PortNumber(8000),
   328  						Hostname: &hostnameFoo,
   329  					},
   330  				}
   331  			},
   332  		},
   333  		{
   334  			desc: "hostname is unique when protocol and port are the same",
   335  			mutate: func(gw *gatewayv1.Gateway) {
   336  				hostnameFoo := gatewayv1.Hostname("foo.com")
   337  				hostnameBar := gatewayv1.Hostname("bar.com")
   338  				gw.Spec.Listeners = []gatewayv1.Listener{
   339  					{
   340  						Name:     gatewayv1.SectionName("foo"),
   341  						Protocol: gatewayv1.HTTPProtocolType,
   342  						Port:     gatewayv1.PortNumber(80),
   343  						Hostname: &hostnameFoo,
   344  					},
   345  					{
   346  						Name:     gatewayv1.SectionName("bar"),
   347  						Protocol: gatewayv1.HTTPProtocolType,
   348  						Port:     gatewayv1.PortNumber(80),
   349  						Hostname: &hostnameBar,
   350  					},
   351  				}
   352  			},
   353  		},
   354  		{
   355  			desc: "one omitted hostname is unique when protocol and port are the same",
   356  			mutate: func(gw *gatewayv1.Gateway) {
   357  				hostnameFoo := gatewayv1.Hostname("foo.com")
   358  				gw.Spec.Listeners = []gatewayv1.Listener{
   359  					{
   360  						Name:     gatewayv1.SectionName("foo"),
   361  						Protocol: gatewayv1.HTTPProtocolType,
   362  						Port:     gatewayv1.PortNumber(80),
   363  						Hostname: &hostnameFoo,
   364  					},
   365  					{
   366  						Name:     gatewayv1.SectionName("bar"),
   367  						Protocol: gatewayv1.HTTPProtocolType,
   368  						Port:     gatewayv1.PortNumber(80),
   369  					},
   370  				}
   371  			},
   372  		},
   373  		{
   374  			desc: "protocol is unique when port and hostname are the same",
   375  			mutate: func(gw *gatewayv1.Gateway) {
   376  				hostnameFoo := gatewayv1.Hostname("foo.com")
   377  				gw.Spec.Listeners = []gatewayv1.Listener{
   378  					{
   379  						Name:     gatewayv1.SectionName("foo"),
   380  						Protocol: gatewayv1.HTTPProtocolType,
   381  						Port:     gatewayv1.PortNumber(8000),
   382  						Hostname: &hostnameFoo,
   383  					},
   384  					{
   385  						Name:     gatewayv1.SectionName("bar"),
   386  						Protocol: gatewayv1.HTTPSProtocolType,
   387  						Port:     gatewayv1.PortNumber(8000),
   388  						Hostname: &hostnameFoo,
   389  						TLS: &gatewayv1.GatewayTLSConfig{
   390  							CertificateRefs: []gatewayv1.SecretObjectReference{
   391  								{Name: gatewayv1.ObjectName("foo")},
   392  							},
   393  						},
   394  					},
   395  				}
   396  			},
   397  		},
   398  		{
   399  			desc: "ip address and hostname in addresses are valid",
   400  			mutate: func(gw *gatewayv1.Gateway) {
   401  				gw.Spec.Addresses = []gatewayv1.GatewayAddress{
   402  					{
   403  						Type:  ptrTo(gatewayv1.IPAddressType),
   404  						Value: "1.2.3.4",
   405  					},
   406  					{
   407  						Type:  ptrTo(gatewayv1.IPAddressType),
   408  						Value: "1111:2222:3333:4444::",
   409  					},
   410  					{
   411  						Type:  ptrTo(gatewayv1.HostnameAddressType),
   412  						Value: "foo.bar",
   413  					},
   414  				}
   415  			},
   416  		},
   417  		{
   418  			desc: "ip address and hostname in addresses are invalid",
   419  			mutate: func(gw *gatewayv1.Gateway) {
   420  				gw.Spec.Addresses = []gatewayv1.GatewayAddress{
   421  					{
   422  						Type:  ptrTo(gatewayv1.IPAddressType),
   423  						Value: "1.2.3.4:8080",
   424  					},
   425  					{
   426  						Type:  ptrTo(gatewayv1.HostnameAddressType),
   427  						Value: "*foo/bar",
   428  					},
   429  					{
   430  						Type:  ptrTo(gatewayv1.HostnameAddressType),
   431  						Value: "12:34:56::",
   432  					},
   433  				}
   434  			},
   435  			wantErrors: []string{"Invalid value: \"1.2.3.4:8080\": spec.addresses[0].value in body must be of type ipv4"},
   436  		},
   437  		{
   438  			desc: "ip address and hostname in status addresses are valid",
   439  			mutateStatus: func(gw *gatewayv1.Gateway) {
   440  				gw.Status.Addresses = []gatewayv1.GatewayStatusAddress{
   441  					{
   442  						Type:  ptrTo(gatewayv1.IPAddressType),
   443  						Value: "1.2.3.4",
   444  					},
   445  					{
   446  						Type:  ptrTo(gatewayv1.IPAddressType),
   447  						Value: "1111:2222:3333:4444::",
   448  					},
   449  					{
   450  						Type:  ptrTo(gatewayv1.HostnameAddressType),
   451  						Value: "foo.bar",
   452  					},
   453  				}
   454  			},
   455  		},
   456  		{
   457  			desc: "ip address and hostname in status addresses are invalid",
   458  			mutateStatus: func(gw *gatewayv1.Gateway) {
   459  				gw.Status.Addresses = []gatewayv1.GatewayStatusAddress{
   460  					{
   461  						Type:  ptrTo(gatewayv1.IPAddressType),
   462  						Value: "1.2.3.4:8080",
   463  					},
   464  					{
   465  						Type:  ptrTo(gatewayv1.HostnameAddressType),
   466  						Value: "*foo/bar",
   467  					},
   468  					{
   469  						Type:  ptrTo(gatewayv1.HostnameAddressType),
   470  						Value: "12:34:56::",
   471  					},
   472  				}
   473  			},
   474  			wantErrors: []string{"Invalid value: \"1.2.3.4:8080\": status.addresses[0].value in body must be of type ipv4"},
   475  		},
   476  		{
   477  			desc: "duplicate ip address or hostname",
   478  			mutate: func(gw *gatewayv1.Gateway) {
   479  				gw.Spec.Addresses = []gatewayv1.GatewayAddress{
   480  					{
   481  						Type:  ptrTo(gatewayv1.IPAddressType),
   482  						Value: "1.2.3.4",
   483  					},
   484  					{
   485  						Type:  ptrTo(gatewayv1.IPAddressType),
   486  						Value: "1.2.3.4",
   487  					},
   488  					{
   489  						Type:  ptrTo(gatewayv1.HostnameAddressType),
   490  						Value: "foo.bar",
   491  					},
   492  					{
   493  						Type:  ptrTo(gatewayv1.HostnameAddressType),
   494  						Value: "foo.bar",
   495  					},
   496  				}
   497  			},
   498  			wantErrors: []string{"IPAddress values must be unique", "Hostname values must be unique"},
   499  		},
   500  	}
   501  
   502  	for _, tc := range testCases {
   503  		t.Run(tc.desc, func(t *testing.T) {
   504  			gw := baseGateway.DeepCopy()
   505  			gw.Name = fmt.Sprintf("foo-%v", time.Now().UnixNano())
   506  
   507  			if tc.mutate != nil {
   508  				tc.mutate(gw)
   509  			}
   510  			err := k8sClient.Create(ctx, gw)
   511  
   512  			if tc.mutateStatus != nil {
   513  				tc.mutateStatus(gw)
   514  				err = k8sClient.Status().Update(ctx, gw)
   515  			}
   516  
   517  			if (len(tc.wantErrors) != 0) != (err != nil) {
   518  				t.Fatalf("Unexpected response while creating Gateway; got err=\n%v\n;want error=%v", err, tc.wantErrors != nil)
   519  			}
   520  
   521  			var missingErrorStrings []string
   522  			for _, wantError := range tc.wantErrors {
   523  				if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(wantError)) {
   524  					missingErrorStrings = append(missingErrorStrings, wantError)
   525  				}
   526  			}
   527  			if len(missingErrorStrings) != 0 {
   528  				t.Errorf("Unexpected response while creating Gateway; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings)
   529  			}
   530  		})
   531  	}
   532  }
   533  

View as plain text