sigs.k8s.io/gateway-api@v1.0.0/pkg/test/cel/gateway_test.go (about) 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 }