sigs.k8s.io/gateway-api@v1.0.0/pkg/test/cel/httproute_experimental_test.go (about) 1 //go:build experimental 2 // +build experimental 3 4 /* 5 Copyright 2023 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package main 21 22 import ( 23 "fmt" 24 "testing" 25 "time" 26 27 gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" 28 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 ) 31 32 //////////////////////////////////////////////////////////////////////////////// 33 //////////////////////////////////////////////////////////////////////////////// 34 // 35 // How are tests named? Where to add new tests? 36 // 37 // Ensure that tests for newly added CEL validations are added in the correctly 38 // named test function. For example, if you added a test at the 39 // `HTTPRouteFilter` hierarchy (i.e. either at the struct level, or on one of 40 // the immediate descendent fields), then the test will go in the 41 // TestHTTPRouteFilter function. If the appropriate test function does not 42 // exist, please create one. 43 // 44 //////////////////////////////////////////////////////////////////////////////// 45 //////////////////////////////////////////////////////////////////////////////// 46 47 func TestHTTPRouteParentRefExperimental(t *testing.T) { 48 tests := []struct { 49 name string 50 wantErrors []string 51 parentRefs []gatewayv1.ParentReference 52 }{ 53 { 54 name: "invalid because duplicate parent refs without port or section name", 55 wantErrors: []string{"sectionName or port must be unique when parentRefs includes 2 or more references to the same parent"}, 56 parentRefs: []gatewayv1.ParentReference{{ 57 Kind: ptrTo(gatewayv1.Kind("Gateway")), 58 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 59 Name: "example", 60 }, { 61 Kind: ptrTo(gatewayv1.Kind("Gateway")), 62 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 63 Name: "example", 64 }}, 65 }, 66 { 67 name: "invalid because duplicate parent refs with only one port", 68 wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"}, 69 parentRefs: []gatewayv1.ParentReference{{ 70 Kind: ptrTo(gatewayv1.Kind("Gateway")), 71 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 72 Name: "example", 73 }, { 74 Kind: ptrTo(gatewayv1.Kind("Gateway")), 75 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 76 Name: "example", 77 Port: ptrTo(gatewayv1.PortNumber(80)), 78 }}, 79 }, 80 { 81 name: "invalid because duplicate parent refs with only one sectionName and port", 82 wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"}, 83 parentRefs: []gatewayv1.ParentReference{{ 84 Kind: ptrTo(gatewayv1.Kind("Gateway")), 85 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 86 Name: "example", 87 }, { 88 Kind: ptrTo(gatewayv1.Kind("Gateway")), 89 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 90 Name: "example", 91 SectionName: ptrTo(gatewayv1.SectionName("foo")), 92 Port: ptrTo(gatewayv1.PortNumber(80)), 93 }}, 94 }, 95 { 96 name: "invalid because duplicate parent refs with duplicate ports", 97 wantErrors: []string{"sectionName or port must be unique when parentRefs includes 2 or more references to the same parent"}, 98 parentRefs: []gatewayv1.ParentReference{{ 99 Kind: ptrTo(gatewayv1.Kind("Gateway")), 100 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 101 Name: "example", 102 Port: ptrTo(gatewayv1.PortNumber(80)), 103 }, { 104 Kind: ptrTo(gatewayv1.Kind("Gateway")), 105 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 106 Name: "example", 107 Port: ptrTo(gatewayv1.PortNumber(80)), 108 }}, 109 }, 110 { 111 name: "valid single parentRef without sectionName or port", 112 wantErrors: []string{}, 113 parentRefs: []gatewayv1.ParentReference{{ 114 Kind: ptrTo(gatewayv1.Kind("Gateway")), 115 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 116 Name: "example", 117 }}, 118 }, 119 { 120 name: "valid single parentRef with sectionName and port", 121 wantErrors: []string{}, 122 parentRefs: []gatewayv1.ParentReference{{ 123 Kind: ptrTo(gatewayv1.Kind("Gateway")), 124 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 125 Name: "example", 126 SectionName: ptrTo(gatewayv1.SectionName("foo")), 127 Port: ptrTo(gatewayv1.PortNumber(443)), 128 }}, 129 }, 130 { 131 name: "valid because duplicate parent refs with different ports", 132 wantErrors: []string{}, 133 parentRefs: []gatewayv1.ParentReference{{ 134 Kind: ptrTo(gatewayv1.Kind("Gateway")), 135 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 136 Name: "example", 137 Port: ptrTo(gatewayv1.PortNumber(80)), 138 }, { 139 Kind: ptrTo(gatewayv1.Kind("Gateway")), 140 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 141 Name: "example", 142 Port: ptrTo(gatewayv1.PortNumber(443)), 143 }}, 144 }, 145 { 146 name: "invalid ParentRefs with multiple mixed references to the same parent", 147 wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"}, 148 parentRefs: []gatewayv1.ParentReference{{ 149 Kind: ptrTo(gatewayv1.Kind("Gateway")), 150 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 151 Name: "example", 152 SectionName: ptrTo(gatewayv1.SectionName("foo")), 153 }, { 154 Kind: ptrTo(gatewayv1.Kind("Gateway")), 155 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 156 Name: "example", 157 Port: ptrTo(gatewayv1.PortNumber(443)), 158 }}, 159 }, 160 { 161 name: "valid ParentRefs with multiple same port references to different section of a parent", 162 wantErrors: []string{}, 163 parentRefs: []gatewayv1.ParentReference{{ 164 Name: "example", 165 Port: ptrTo(gatewayv1.PortNumber(443)), 166 SectionName: ptrTo(gatewayv1.SectionName("foo")), 167 }, { 168 Name: "example", 169 Port: ptrTo(gatewayv1.PortNumber(443)), 170 SectionName: ptrTo(gatewayv1.SectionName("bar")), 171 }}, 172 }, 173 { 174 // when referencing the same object, both parentRefs need to specify 175 // the same optional fields (both parentRefs must specify port, 176 // sectionName, or both) 177 name: "invalid because duplicate parent refs with first having sectionName and second having both sectionName and port", 178 wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"}, 179 parentRefs: []gatewayv1.ParentReference{{ 180 Kind: ptrTo(gatewayv1.Kind("Gateway")), 181 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 182 Name: "example", 183 SectionName: ptrTo(gatewayv1.SectionName("foo")), 184 }, { 185 Kind: ptrTo(gatewayv1.Kind("Gateway")), 186 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 187 Name: "example", 188 Port: ptrTo(gatewayv1.PortNumber(443)), 189 SectionName: ptrTo(gatewayv1.SectionName("foo")), 190 }}, 191 }, 192 { 193 name: "valid because first parentRef has namespace while second doesn't", 194 wantErrors: []string{}, 195 parentRefs: []gatewayv1.ParentReference{{ 196 Kind: ptrTo(gatewayv1.Kind("Gateway")), 197 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 198 Name: "example", 199 Namespace: ptrTo(gatewayv1.Namespace("test")), 200 }, { 201 Kind: ptrTo(gatewayv1.Kind("Gateway")), 202 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 203 Name: "example", 204 }}, 205 }, 206 { 207 name: "valid because second parentRef has namespace while first doesn't", 208 wantErrors: []string{}, 209 parentRefs: []gatewayv1.ParentReference{{ 210 Kind: ptrTo(gatewayv1.Kind("Gateway")), 211 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 212 Name: "example", 213 }, { 214 Kind: ptrTo(gatewayv1.Kind("Gateway")), 215 Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")), 216 Name: "example", 217 Namespace: ptrTo(gatewayv1.Namespace("test")), 218 }}, 219 }, 220 } 221 222 for _, tc := range tests { 223 t.Run(tc.name, func(t *testing.T) { 224 route := &gatewayv1.HTTPRoute{ 225 ObjectMeta: metav1.ObjectMeta{ 226 Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()), 227 Namespace: metav1.NamespaceDefault, 228 }, 229 Spec: gatewayv1.HTTPRouteSpec{ 230 CommonRouteSpec: gatewayv1.CommonRouteSpec{ 231 ParentRefs: tc.parentRefs, 232 }, 233 }, 234 } 235 validateHTTPRoute(t, route, tc.wantErrors) 236 }) 237 } 238 } 239 240 func toDuration(durationString string) *gatewayv1.Duration { 241 return (*gatewayv1.Duration)(&durationString) 242 } 243 244 func TestHTTPRouteTimeouts(t *testing.T) { 245 tests := []struct { 246 name string 247 wantErrors []string 248 rules []gatewayv1.HTTPRouteRule 249 }{ 250 { 251 name: "invalid timeout unit us is not supported", 252 wantErrors: []string{"Invalid value: \"100us\": spec.rules[0].timeouts.request in body should match '^([0-9]{1,5}(h|m|s|ms)){1,4}$'"}, 253 rules: []gatewayv1.HTTPRouteRule{ 254 { 255 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 256 Request: toDuration("100us"), 257 }, 258 }, 259 }, 260 }, 261 { 262 name: "invalid timeout unit ns is not supported", 263 wantErrors: []string{"Invalid value: \"500ns\": spec.rules[0].timeouts.request in body should match '^([0-9]{1,5}(h|m|s|ms)){1,4}$'"}, 264 rules: []gatewayv1.HTTPRouteRule{ 265 { 266 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 267 Request: toDuration("500ns"), 268 }, 269 }, 270 }, 271 }, 272 { 273 name: "valid timeout request and backendRequest", 274 rules: []gatewayv1.HTTPRouteRule{ 275 { 276 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 277 Request: toDuration("4s"), 278 BackendRequest: toDuration("2s"), 279 }, 280 }, 281 }, 282 }, 283 { 284 name: "valid timeout request", 285 rules: []gatewayv1.HTTPRouteRule{ 286 { 287 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 288 Request: toDuration("0s"), 289 }, 290 }, 291 }, 292 }, 293 { 294 name: "invalid timeout request day unit not supported", 295 wantErrors: []string{"Invalid value: \"1d\": spec.rules[0].timeouts.request in body should match '^([0-9]{1,5}(h|m|s|ms)){1,4}$'"}, 296 rules: []gatewayv1.HTTPRouteRule{ 297 { 298 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 299 Request: toDuration("1d"), 300 }, 301 }, 302 }, 303 }, 304 { 305 name: "invalid timeout request decimal not supported ", 306 wantErrors: []string{"Invalid value: \"0.5s\": spec.rules[0].timeouts.request in body should match '^([0-9]{1,5}(h|m|s|ms)){1,4}$'"}, 307 rules: []gatewayv1.HTTPRouteRule{ 308 { 309 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 310 Request: toDuration("0.5s"), 311 }, 312 }, 313 }, 314 }, 315 { 316 name: "valid timeout request infinite greater than backendRequest 1ms", 317 rules: []gatewayv1.HTTPRouteRule{ 318 { 319 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 320 Request: toDuration("0s"), 321 BackendRequest: toDuration("1ms"), 322 }, 323 }, 324 }, 325 }, 326 { 327 name: "valid timeout request 1s greater than backendRequest 200ms", 328 rules: []gatewayv1.HTTPRouteRule{ 329 { 330 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 331 Request: toDuration("1s"), 332 BackendRequest: toDuration("200ms"), 333 }, 334 }, 335 }, 336 }, 337 { 338 name: "valid timeout request 10s equal backendRequest 10s", 339 rules: []gatewayv1.HTTPRouteRule{ 340 { 341 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 342 Request: toDuration("10s"), 343 BackendRequest: toDuration("10s"), 344 }, 345 }, 346 }, 347 }, 348 { 349 name: "invalid timeout request 200ms less than backendRequest 1s", 350 wantErrors: []string{"Invalid value: \"object\": backendRequest timeout cannot be longer than request timeout"}, 351 rules: []gatewayv1.HTTPRouteRule{ 352 { 353 Timeouts: &gatewayv1.HTTPRouteTimeouts{ 354 Request: toDuration("200ms"), 355 BackendRequest: toDuration("1s"), 356 }, 357 }, 358 }, 359 }, 360 } 361 362 for _, tc := range tests { 363 t.Run(tc.name, func(t *testing.T) { 364 route := &gatewayv1.HTTPRoute{ 365 ObjectMeta: metav1.ObjectMeta{ 366 Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()), 367 Namespace: metav1.NamespaceDefault, 368 }, 369 Spec: gatewayv1.HTTPRouteSpec{Rules: tc.rules}, 370 } 371 validateHTTPRoute(t, route, tc.wantErrors) 372 }) 373 } 374 }