sigs.k8s.io/gateway-api@v1.0.0/conformance/tests/httproute-hostname-intersection.go (about) 1 /* 2 Copyright 2022 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 tests 18 19 import ( 20 "testing" 21 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 "k8s.io/apimachinery/pkg/types" 24 25 v1 "sigs.k8s.io/gateway-api/apis/v1" 26 "sigs.k8s.io/gateway-api/conformance/utils/http" 27 "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" 28 "sigs.k8s.io/gateway-api/conformance/utils/suite" 29 ) 30 31 func init() { 32 ConformanceTests = append(ConformanceTests, HTTPRouteHostnameIntersection) 33 } 34 35 var HTTPRouteHostnameIntersection = suite.ConformanceTest{ 36 ShortName: "HTTPRouteHostnameIntersection", 37 Description: "HTTPRoutes should attach to listeners only if they have intersecting hostnames, and should accept requests only for the intersecting hostnames", 38 Features: []suite.SupportedFeature{ 39 suite.SupportGateway, 40 suite.SupportHTTPRoute, 41 }, 42 Manifests: []string{"tests/httproute-hostname-intersection.yaml"}, 43 Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { 44 ns := "gateway-conformance-infra" 45 gwNN := types.NamespacedName{Name: "httproute-hostname-intersection", Namespace: ns} 46 47 // This test creates an additional Gateway in the gateway-conformance-infra 48 // namespace so we have to wait for it to be ready. 49 kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{ns}) 50 51 t.Run("HTTPRoutes that do intersect with listener hostnames", func(t *testing.T) { 52 routes := []types.NamespacedName{ 53 {Namespace: ns, Name: "specific-host-matches-listener-specific-host"}, 54 {Namespace: ns, Name: "specific-host-matches-listener-wildcard-host"}, 55 {Namespace: ns, Name: "wildcard-host-matches-listener-specific-host"}, 56 {Namespace: ns, Name: "wildcard-host-matches-listener-wildcard-host"}, 57 } 58 gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routes...) 59 for _, routeNN := range routes { 60 kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) 61 } 62 63 var testCases []http.ExpectedResponse 64 65 // Test cases for HTTPRoute "specific-host-matches-listener-specific-host". 66 testCases = append(testCases, 67 http.ExpectedResponse{ 68 Request: http.Request{Host: "very.specific.com", Path: "/s1"}, 69 Backend: "infra-backend-v1", 70 Namespace: ns, 71 }, 72 // Port value within the Host header MUST not be considered while 73 // performing match against hostname. 74 http.ExpectedResponse{ 75 Request: http.Request{Host: "very.specific.com:1234", Path: "/s1"}, 76 Backend: "infra-backend-v1", 77 Namespace: ns, 78 }, 79 http.ExpectedResponse{ 80 Request: http.Request{Host: "non.matching.com", Path: "/s1"}, 81 Response: http.Response{StatusCode: 404}, 82 }, 83 http.ExpectedResponse{ 84 Request: http.Request{Host: "foo.nonmatchingwildcard.io", Path: "/s1"}, 85 Response: http.Response{StatusCode: 404}, 86 }, 87 http.ExpectedResponse{ 88 Request: http.Request{Host: "foo.wildcard.io", Path: "/s1"}, 89 Response: http.Response{StatusCode: 404}, 90 }, 91 http.ExpectedResponse{ 92 Request: http.Request{Host: "very.specific.com", Path: "/non-matching-prefix"}, 93 Response: http.Response{StatusCode: 404}, 94 }, 95 ) 96 97 // Test cases for HTTPRoute "specific-host-matches-listener-wildcard-host". 98 testCases = append(testCases, 99 http.ExpectedResponse{ 100 Request: http.Request{Host: "foo.wildcard.io", Path: "/s2"}, 101 Backend: "infra-backend-v2", 102 Namespace: ns, 103 }, 104 http.ExpectedResponse{ 105 Request: http.Request{Host: "bar.wildcard.io", Path: "/s2"}, 106 Backend: "infra-backend-v2", 107 Namespace: ns, 108 }, 109 http.ExpectedResponse{ 110 Request: http.Request{Host: "foo.bar.wildcard.io", Path: "/s2"}, 111 Backend: "infra-backend-v2", 112 Namespace: ns, 113 }, 114 http.ExpectedResponse{ 115 Request: http.Request{Host: "non.matching.com", Path: "/s2"}, 116 Response: http.Response{StatusCode: 404}, 117 }, 118 http.ExpectedResponse{ 119 Request: http.Request{Host: "wildcard.io", Path: "/s2"}, 120 Response: http.Response{StatusCode: 404}, 121 }, 122 123 http.ExpectedResponse{ 124 Request: http.Request{Host: "very.specific.com", Path: "/s2"}, 125 Response: http.Response{StatusCode: 404}, 126 }, 127 http.ExpectedResponse{ 128 Request: http.Request{Host: "foo.wildcard.io", Path: "/non-matching-prefix"}, 129 Response: http.Response{StatusCode: 404}, 130 }, 131 ) 132 133 // Test cases for HTTPRoute "wildcard-host-matches-listener-specific-host". 134 testCases = append(testCases, 135 http.ExpectedResponse{ 136 Request: http.Request{Host: "very.specific.com", Path: "/s3"}, 137 Backend: "infra-backend-v3", 138 Namespace: ns, 139 }, 140 http.ExpectedResponse{ 141 Request: http.Request{Host: "non.matching.com", Path: "/s3"}, 142 Response: http.Response{StatusCode: 404}, 143 }, 144 http.ExpectedResponse{ 145 Request: http.Request{Host: "foo.specific.com", Path: "/s3"}, 146 Response: http.Response{StatusCode: 404}, 147 }, 148 http.ExpectedResponse{ 149 Request: http.Request{Host: "foo.wildcard.io", Path: "/s3"}, 150 Response: http.Response{StatusCode: 404}, 151 }, 152 http.ExpectedResponse{ 153 Request: http.Request{Host: "very.specific.com", Path: "/non-matching-prefix"}, 154 Response: http.Response{StatusCode: 404}, 155 }, 156 ) 157 158 // Test cases for HTTPRoute "wildcard-host-matches-listener-wildcard-host". 159 testCases = append(testCases, 160 http.ExpectedResponse{ 161 Request: http.Request{Host: "foo.anotherwildcard.io", Path: "/s4"}, 162 Backend: "infra-backend-v1", 163 Namespace: ns, 164 }, 165 http.ExpectedResponse{ 166 Request: http.Request{Host: "bar.anotherwildcard.io", Path: "/s4"}, 167 Backend: "infra-backend-v1", 168 Namespace: ns, 169 }, 170 http.ExpectedResponse{ 171 Request: http.Request{Host: "foo.bar.anotherwildcard.io", Path: "/s4"}, 172 Backend: "infra-backend-v1", 173 Namespace: ns, 174 }, 175 http.ExpectedResponse{ 176 Request: http.Request{Host: "anotherwildcard.io", Path: "/s4"}, 177 Response: http.Response{StatusCode: 404}, 178 }, 179 180 http.ExpectedResponse{ 181 Request: http.Request{Host: "foo.wildcard.io", Path: "/s4"}, 182 Response: http.Response{StatusCode: 404}, 183 }, 184 http.ExpectedResponse{ 185 Request: http.Request{Host: "very.specific.com", Path: "/s4"}, 186 Response: http.Response{StatusCode: 404}, 187 }, 188 http.ExpectedResponse{ 189 Request: http.Request{Host: "foo.anotherwildcard.io", Path: "/non-matching-prefix"}, 190 Response: http.Response{StatusCode: 404}, 191 }, 192 ) 193 194 for i := range testCases { 195 // Declare tc here to avoid loop variable 196 // reuse issues across parallel tests. 197 tc := testCases[i] 198 t.Run(tc.GetTestCaseName(i), func(t *testing.T) { 199 t.Parallel() 200 http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc) 201 }) 202 } 203 }) 204 205 t.Run("HTTPRoutes that do not intersect with listener hostnames", func(t *testing.T) { 206 gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN)) 207 routeNN := types.NamespacedName{Namespace: ns, Name: "no-intersecting-hosts"} 208 209 parents := []v1.RouteParentStatus{{ 210 ParentRef: parentRefTo(gwNN), 211 ControllerName: v1.GatewayController(suite.ControllerName), 212 Conditions: []metav1.Condition{ 213 { 214 Type: string(v1.RouteConditionAccepted), 215 Status: metav1.ConditionFalse, 216 Reason: string(v1.RouteReasonNoMatchingListenerHostname), 217 }, 218 }, 219 }} 220 221 kubernetes.HTTPRouteMustHaveParents(t, suite.Client, suite.TimeoutConfig, routeNN, parents, true) 222 223 testCases := []http.ExpectedResponse{ 224 { 225 Request: http.Request{Host: "specific.but.wrong.com", Path: "/s5"}, 226 Response: http.Response{StatusCode: 404}, 227 }, 228 { 229 Request: http.Request{Host: "wildcard.io", Path: "/s5"}, 230 Response: http.Response{StatusCode: 404}, 231 }, 232 } 233 234 for i := range testCases { 235 // Declare tc here to avoid loop variable 236 // reuse issues across parallel tests. 237 tc := testCases[i] 238 t.Run(tc.GetTestCaseName(i), func(t *testing.T) { 239 t.Parallel() 240 http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc) 241 }) 242 } 243 }) 244 }, 245 } 246 247 func parentRefTo(gateway types.NamespacedName) v1.ParentReference { 248 var ( 249 group = v1.Group(v1.GroupName) 250 kind = v1.Kind("Gateway") 251 namespace = v1.Namespace(gateway.Namespace) 252 name = v1.ObjectName(gateway.Name) 253 ) 254 255 return v1.ParentReference{ 256 Group: &group, 257 Kind: &kind, 258 Namespace: &namespace, 259 Name: name, 260 } 261 }