sigs.k8s.io/gateway-api@v1.0.0/apis/v1/validation/gateway_test.go (about) 1 /* 2 Copyright 2021 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 validation 18 19 import ( 20 "fmt" 21 "testing" 22 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/util/validation/field" 25 26 gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" 27 ) 28 29 func TestValidateGateway(t *testing.T) { 30 listeners := []gatewayv1.Listener{ 31 { 32 Hostname: nil, 33 }, 34 } 35 addresses := []gatewayv1.GatewayAddress{ 36 { 37 Type: nil, 38 }, 39 } 40 baseGateway := gatewayv1.Gateway{ 41 ObjectMeta: metav1.ObjectMeta{ 42 Name: "foo", 43 Namespace: metav1.NamespaceDefault, 44 }, 45 Spec: gatewayv1.GatewaySpec{ 46 GatewayClassName: "foo", 47 Listeners: listeners, 48 Addresses: addresses, 49 }, 50 } 51 tlsConfig := gatewayv1.GatewayTLSConfig{} 52 53 testCases := map[string]struct { 54 mutate func(gw *gatewayv1.Gateway) 55 expectErrs []field.Error 56 }{ 57 "tls config present with http protocol": { 58 mutate: func(gw *gatewayv1.Gateway) { 59 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType 60 gw.Spec.Listeners[0].TLS = &tlsConfig 61 }, 62 expectErrs: []field.Error{ 63 { 64 Type: field.ErrorTypeForbidden, 65 Field: "spec.listeners[0].tls", 66 Detail: "should be empty for protocol HTTP", 67 BadValue: "", 68 }, 69 }, 70 }, 71 "tls config present with tcp protocol": { 72 mutate: func(gw *gatewayv1.Gateway) { 73 gw.Spec.Listeners[0].Protocol = gatewayv1.TCPProtocolType 74 gw.Spec.Listeners[0].TLS = &tlsConfig 75 }, 76 expectErrs: []field.Error{ 77 { 78 Type: field.ErrorTypeForbidden, 79 Field: "spec.listeners[0].tls", 80 Detail: "should be empty for protocol TCP", 81 BadValue: "", 82 }, 83 }, 84 }, 85 "tls config not set with https protocol": { 86 mutate: func(gw *gatewayv1.Gateway) { 87 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPSProtocolType 88 }, 89 expectErrs: []field.Error{ 90 { 91 Type: field.ErrorTypeForbidden, 92 Field: "spec.listeners[0].tls", 93 Detail: "must be set for protocol HTTPS", 94 BadValue: "", 95 }, 96 }, 97 }, 98 "tls config not set with tls protocol": { 99 mutate: func(gw *gatewayv1.Gateway) { 100 gw.Spec.Listeners[0].Protocol = gatewayv1.TLSProtocolType 101 }, 102 expectErrs: []field.Error{ 103 { 104 Type: field.ErrorTypeForbidden, 105 Field: "spec.listeners[0].tls", 106 Detail: "must be set for protocol TLS", 107 BadValue: "", 108 }, 109 }, 110 }, 111 "tls config not set with http protocol": { 112 mutate: func(gw *gatewayv1.Gateway) { 113 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType 114 }, 115 expectErrs: nil, 116 }, 117 "tls config not set with tcp protocol": { 118 mutate: func(gw *gatewayv1.Gateway) { 119 gw.Spec.Listeners[0].Protocol = gatewayv1.TCPProtocolType 120 }, 121 expectErrs: nil, 122 }, 123 "tls config not set with udp protocol": { 124 mutate: func(gw *gatewayv1.Gateway) { 125 gw.Spec.Listeners[0].Protocol = gatewayv1.UDPProtocolType 126 }, 127 expectErrs: nil, 128 }, 129 "hostname present with tcp protocol": { 130 mutate: func(gw *gatewayv1.Gateway) { 131 hostname := gatewayv1.Hostname("foo.bar.com") 132 gw.Spec.Listeners[0].Hostname = &hostname 133 gw.Spec.Listeners[0].Protocol = gatewayv1.TCPProtocolType 134 }, 135 expectErrs: []field.Error{ 136 { 137 Type: field.ErrorTypeForbidden, 138 Field: "spec.listeners[0].hostname", 139 Detail: "should be empty for protocol TCP", 140 BadValue: "", 141 }, 142 }, 143 }, 144 "hostname present with udp protocol": { 145 mutate: func(gw *gatewayv1.Gateway) { 146 hostname := gatewayv1.Hostname("foo.bar.com") 147 gw.Spec.Listeners[0].Hostname = &hostname 148 gw.Spec.Listeners[0].Protocol = gatewayv1.UDPProtocolType 149 }, 150 expectErrs: []field.Error{ 151 { 152 Type: field.ErrorTypeForbidden, 153 Field: "spec.listeners[0].hostname", 154 Detail: "should be empty for protocol UDP", 155 BadValue: "", 156 }, 157 }, 158 }, 159 "certificatedRefs not set with https protocol and TLS terminate mode": { 160 mutate: func(gw *gatewayv1.Gateway) { 161 hostname := gatewayv1.Hostname("foo.bar.com") 162 tlsMode := gatewayv1.TLSModeType("Terminate") 163 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPSProtocolType 164 gw.Spec.Listeners[0].Hostname = &hostname 165 gw.Spec.Listeners[0].TLS = &tlsConfig 166 gw.Spec.Listeners[0].TLS.Mode = &tlsMode 167 }, 168 expectErrs: []field.Error{ 169 { 170 Type: field.ErrorTypeForbidden, 171 Field: "spec.listeners[0].tls.certificateRefs", 172 Detail: "should be set and not empty when TLSModeType is Terminate", 173 BadValue: "", 174 }, 175 }, 176 }, 177 "certificatedRefs not set with tls protocol and TLS terminate mode": { 178 mutate: func(gw *gatewayv1.Gateway) { 179 hostname := gatewayv1.Hostname("foo.bar.com") 180 tlsMode := gatewayv1.TLSModeType("Terminate") 181 gw.Spec.Listeners[0].Protocol = gatewayv1.TLSProtocolType 182 gw.Spec.Listeners[0].Hostname = &hostname 183 gw.Spec.Listeners[0].TLS = &tlsConfig 184 gw.Spec.Listeners[0].TLS.Mode = &tlsMode 185 }, 186 expectErrs: []field.Error{ 187 { 188 Type: field.ErrorTypeForbidden, 189 Field: "spec.listeners[0].tls.certificateRefs", 190 Detail: "should be set and not empty when TLSModeType is Terminate", 191 BadValue: "", 192 }, 193 }, 194 }, 195 "names are not unique within the Gateway": { 196 mutate: func(gw *gatewayv1.Gateway) { 197 hostnameFoo := gatewayv1.Hostname("foo.com") 198 hostnameBar := gatewayv1.Hostname("bar.com") 199 gw.Spec.Listeners[0].Name = "foo" 200 gw.Spec.Listeners[0].Hostname = &hostnameFoo 201 gw.Spec.Listeners = append(gw.Spec.Listeners, 202 gatewayv1.Listener{ 203 Name: "foo", 204 Hostname: &hostnameBar, 205 }, 206 ) 207 }, 208 expectErrs: []field.Error{ 209 { 210 Type: field.ErrorTypeDuplicate, 211 Field: "spec.listeners[1].name", 212 BadValue: "must be unique within the Gateway", 213 }, 214 }, 215 }, 216 "combination of port, protocol, and hostname are not unique for each listener": { 217 mutate: func(gw *gatewayv1.Gateway) { 218 hostnameFoo := gatewayv1.Hostname("foo.com") 219 gw.Spec.Listeners[0].Name = "foo" 220 gw.Spec.Listeners[0].Hostname = &hostnameFoo 221 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType 222 gw.Spec.Listeners[0].Port = 80 223 gw.Spec.Listeners = append(gw.Spec.Listeners, 224 gatewayv1.Listener{ 225 Name: "bar", 226 Hostname: &hostnameFoo, 227 Protocol: gatewayv1.HTTPProtocolType, 228 Port: 80, 229 }, 230 ) 231 }, 232 expectErrs: []field.Error{ 233 { 234 Type: field.ErrorTypeDuplicate, 235 Field: "spec.listeners[1]", 236 BadValue: "combination of port, protocol, and hostname must be unique for each listener", 237 }, 238 }, 239 }, 240 "combination of port and protocol are not unique for each listener when hostnames not set": { 241 mutate: func(gw *gatewayv1.Gateway) { 242 gw.Spec.Listeners[0].Name = "foo" 243 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType 244 gw.Spec.Listeners[0].Port = 80 245 gw.Spec.Listeners = append(gw.Spec.Listeners, 246 gatewayv1.Listener{ 247 Name: "bar", 248 Protocol: gatewayv1.HTTPProtocolType, 249 Port: 80, 250 }, 251 ) 252 }, 253 expectErrs: []field.Error{ 254 { 255 Type: field.ErrorTypeDuplicate, 256 Field: "spec.listeners[1]", 257 BadValue: "combination of port, protocol, and hostname must be unique for each listener", 258 }, 259 }, 260 }, 261 "port is unique when protocol and hostname are the same": { 262 mutate: func(gw *gatewayv1.Gateway) { 263 hostnameFoo := gatewayv1.Hostname("foo.com") 264 gw.Spec.Listeners[0].Name = "foo" 265 gw.Spec.Listeners[0].Hostname = &hostnameFoo 266 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType 267 gw.Spec.Listeners[0].Port = 80 268 gw.Spec.Listeners = append(gw.Spec.Listeners, 269 gatewayv1.Listener{ 270 Name: "bar", 271 Hostname: &hostnameFoo, 272 Protocol: gatewayv1.HTTPProtocolType, 273 Port: 8080, 274 }, 275 ) 276 }, 277 expectErrs: nil, 278 }, 279 "hostname is unique when protocol and port are the same": { 280 mutate: func(gw *gatewayv1.Gateway) { 281 hostnameFoo := gatewayv1.Hostname("foo.com") 282 hostnameBar := gatewayv1.Hostname("bar.com") 283 gw.Spec.Listeners[0].Name = "foo" 284 gw.Spec.Listeners[0].Hostname = &hostnameFoo 285 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType 286 gw.Spec.Listeners[0].Port = 80 287 gw.Spec.Listeners = append(gw.Spec.Listeners, 288 gatewayv1.Listener{ 289 Name: "bar", 290 Hostname: &hostnameBar, 291 Protocol: gatewayv1.HTTPProtocolType, 292 Port: 80, 293 }, 294 ) 295 }, 296 expectErrs: nil, 297 }, 298 "protocol is unique when port and hostname are the same": { 299 mutate: func(gw *gatewayv1.Gateway) { 300 hostnameFoo := gatewayv1.Hostname("foo.com") 301 tlsConfigFoo := tlsConfig 302 tlsModeFoo := gatewayv1.TLSModeType("Terminate") 303 tlsConfigFoo.Mode = &tlsModeFoo 304 tlsConfigFoo.CertificateRefs = []gatewayv1.SecretObjectReference{ 305 { 306 Name: "FooCertificateRefs", 307 }, 308 } 309 gw.Spec.Listeners[0].Name = "foo" 310 gw.Spec.Listeners[0].Hostname = &hostnameFoo 311 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPSProtocolType 312 gw.Spec.Listeners[0].Port = 8000 313 gw.Spec.Listeners[0].TLS = &tlsConfigFoo 314 gw.Spec.Listeners = append(gw.Spec.Listeners, 315 gatewayv1.Listener{ 316 Name: "bar", 317 Hostname: &hostnameFoo, 318 Protocol: gatewayv1.TLSProtocolType, 319 Port: 8000, 320 TLS: &tlsConfigFoo, 321 }, 322 ) 323 }, 324 expectErrs: nil, 325 }, 326 "ip address and hostname in addresses are valid": { 327 mutate: func(gw *gatewayv1.Gateway) { 328 gw.Spec.Addresses = []gatewayv1.GatewayAddress{ 329 { 330 Type: ptrTo(gatewayv1.IPAddressType), 331 Value: "1.2.3.4", 332 }, 333 { 334 Type: ptrTo(gatewayv1.IPAddressType), 335 Value: "1111:2222:3333:4444::", 336 }, 337 { 338 Type: ptrTo(gatewayv1.HostnameAddressType), 339 Value: "foo.bar", 340 }, 341 } 342 }, 343 expectErrs: nil, 344 }, 345 "ip address and hostname in addresses are invalid": { 346 mutate: func(gw *gatewayv1.Gateway) { 347 gw.Spec.Addresses = []gatewayv1.GatewayAddress{ 348 { 349 Type: ptrTo(gatewayv1.IPAddressType), 350 Value: "1.2.3.4:8080", 351 }, 352 { 353 Type: ptrTo(gatewayv1.HostnameAddressType), 354 Value: "*foo/bar", 355 }, 356 { 357 Type: ptrTo(gatewayv1.HostnameAddressType), 358 Value: "12:34:56::", 359 }, 360 } 361 }, 362 expectErrs: []field.Error{ 363 { 364 Type: field.ErrorTypeInvalid, 365 Field: "spec.addresses[0]", 366 Detail: "invalid ip address", 367 BadValue: "1.2.3.4:8080", 368 }, 369 { 370 Type: field.ErrorTypeInvalid, 371 Field: "spec.addresses[1]", 372 Detail: fmt.Sprintf("must only contain valid characters (matching %s)", validHostnameAddress), 373 BadValue: "*foo/bar", 374 }, 375 { 376 Type: field.ErrorTypeInvalid, 377 Field: "spec.addresses[2]", 378 Detail: fmt.Sprintf("must only contain valid characters (matching %s)", validHostnameAddress), 379 BadValue: "12:34:56::", 380 }, 381 }, 382 }, 383 "duplicate ip address or hostname": { 384 mutate: func(gw *gatewayv1.Gateway) { 385 gw.Spec.Addresses = []gatewayv1.GatewayAddress{ 386 { 387 Type: ptrTo(gatewayv1.IPAddressType), 388 Value: "1.2.3.4", 389 }, 390 { 391 Type: ptrTo(gatewayv1.IPAddressType), 392 Value: "1.2.3.4", 393 }, 394 { 395 Type: ptrTo(gatewayv1.HostnameAddressType), 396 Value: "foo.bar", 397 }, 398 { 399 Type: ptrTo(gatewayv1.HostnameAddressType), 400 Value: "foo.bar", 401 }, 402 } 403 }, 404 expectErrs: []field.Error{ 405 { 406 Type: field.ErrorTypeDuplicate, 407 Field: "spec.addresses[1]", 408 BadValue: "1.2.3.4", 409 }, 410 { 411 Type: field.ErrorTypeDuplicate, 412 Field: "spec.addresses[3]", 413 BadValue: "foo.bar", 414 }, 415 }, 416 }, 417 } 418 419 for name, tc := range testCases { 420 tc := tc 421 t.Run(name, func(t *testing.T) { 422 gw := baseGateway.DeepCopy() 423 tc.mutate(gw) 424 errs := ValidateGateway(gw) 425 if len(tc.expectErrs) != len(errs) { 426 t.Fatalf("Expected %d errors, got %d errors: %v", len(tc.expectErrs), len(errs), errs) 427 } 428 for i, err := range errs { 429 if err.Type != tc.expectErrs[i].Type { 430 t.Errorf("Expected error on type: %s, got: %s", tc.expectErrs[i].Type, err.Type) 431 } 432 if err.Field != tc.expectErrs[i].Field { 433 t.Errorf("Expected error on field: %s, got: %s", tc.expectErrs[i].Field, err.Field) 434 } 435 if err.Detail != tc.expectErrs[i].Detail { 436 t.Errorf("Expected error on detail: %s, got: %s", tc.expectErrs[i].Detail, err.Detail) 437 } 438 if err.BadValue != tc.expectErrs[i].BadValue { 439 t.Errorf("Expected error on bad value: %s, got: %s", tc.expectErrs[i].BadValue, err.BadValue) 440 } 441 } 442 }) 443 } 444 }