tests

package
v0.8.0-rc1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2023 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ConformanceTests []suite.ConformanceTest
View Source
var GatewayClassObservedGenerationBump = suite.ConformanceTest{
	ShortName: "GatewayClassObservedGenerationBump",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
	},
	Description: "A GatewayClass should update the observedGeneration in all of it's Status.Conditions after an update to the spec",
	Manifests:   []string{"tests/gatewayclass-observed-generation-bump.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		gwc := types.NamespacedName{Name: "gatewayclass-observed-generation-bump"}

		t.Run("observedGeneration should increment", func(t *testing.T) {
			ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
			defer cancel()

			kubernetes.GWCMustHaveAcceptedConditionAny(t, s.Client, s.TimeoutConfig, gwc.Name)

			original := &v1beta1.GatewayClass{}
			err := s.Client.Get(ctx, gwc, original)
			require.NoErrorf(t, err, "error getting GatewayClass: %v", err)

			kubernetes.GatewayClassMustHaveLatestConditions(t, original)

			mutate := original.DeepCopy()
			desc := "new"
			mutate.Spec.Description = &desc

			err = s.Client.Patch(ctx, mutate, client.MergeFrom(original))
			require.NoErrorf(t, err, "error patching the GatewayClass: %v", err)

			kubernetes.GWCMustHaveAcceptedConditionAny(t, s.Client, s.TimeoutConfig, gwc.Name)

			updated := &v1beta1.GatewayClass{}
			err = s.Client.Get(ctx, gwc, updated)
			require.NoErrorf(t, err, "error getting GatewayClass: %v", err)

			kubernetes.GatewayClassMustHaveLatestConditions(t, updated)

			require.NotEqual(t, original.Generation, updated.Generation, "generation should change after an update")
		})
	},
}
View Source
var GatewayInvalidRouteKind = suite.ConformanceTest{
	ShortName:   "GatewayInvalidRouteKind",
	Description: "A Gateway in the gateway-conformance-infra namespace should fail to become ready an invalid Route kind is specified.",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
	},
	Manifests: []string{"tests/gateway-invalid-route-kind.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		t.Run("Gateway listener should have a false ResolvedRefs condition with reason InvalidRouteKinds and no supportedKinds", func(t *testing.T) {
			gwNN := types.NamespacedName{Name: "gateway-only-invalid-route-kind", Namespace: "gateway-conformance-infra"}
			listeners := []v1beta1.ListenerStatus{{
				Name:           v1beta1.SectionName("http"),
				SupportedKinds: []v1beta1.RouteGroupKind{},
				Conditions: []metav1.Condition{{
					Type:   string(v1beta1.ListenerConditionResolvedRefs),
					Status: metav1.ConditionFalse,
					Reason: string(v1beta1.ListenerReasonInvalidRouteKinds),
				}},
				AttachedRoutes: 0,
			}}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)
		})

		t.Run("Gateway listener should have a false ResolvedRefs condition with reason InvalidRouteKinds and HTTPRoute must be put in the supportedKinds", func(t *testing.T) {
			gwNN := types.NamespacedName{Name: "gateway-supported-and-invalid-route-kind", Namespace: "gateway-conformance-infra"}
			listeners := []v1beta1.ListenerStatus{{
				Name: v1beta1.SectionName("http"),
				SupportedKinds: []v1beta1.RouteGroupKind{{
					Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
					Kind:  v1beta1.Kind("HTTPRoute"),
				}},
				Conditions: []metav1.Condition{{
					Type:   string(v1beta1.ListenerConditionResolvedRefs),
					Status: metav1.ConditionFalse,
					Reason: string(v1beta1.ListenerReasonInvalidRouteKinds),
				}},
				AttachedRoutes: 0,
			}}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)
		})
	},
}
View Source
var GatewayInvalidTLSConfiguration = suite.ConformanceTest{
	ShortName:   "GatewayInvalidTLSConfiguration",
	Description: "A Gateway should fail to become ready if the Gateway has an invalid TLS configuration",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
	},
	Manifests: []string{"tests/gateway-invalid-tls-certificateref.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		listeners := []v1beta1.ListenerStatus{{
			Name: v1beta1.SectionName("https"),
			SupportedKinds: []v1beta1.RouteGroupKind{{
				Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
				Kind:  v1beta1.Kind("HTTPRoute"),
			}},
			Conditions: []metav1.Condition{{
				Type:   string(v1beta1.ListenerConditionResolvedRefs),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.ListenerReasonInvalidCertificateRef),
			}},
			AttachedRoutes: 0,
		}}

		testCases := []struct {
			name                  string
			gatewayNamespacedName types.NamespacedName
		}{
			{
				name:                  "Nonexistent secret referenced as CertificateRef in a Gateway listener",
				gatewayNamespacedName: types.NamespacedName{Name: "gateway-certificate-nonexistent-secret", Namespace: "gateway-conformance-infra"},
			},
			{
				name:                  "Unsupported group resource referenced as CertificateRef in a Gateway listener",
				gatewayNamespacedName: types.NamespacedName{Name: "gateway-certificate-unsupported-group", Namespace: "gateway-conformance-infra"},
			},
			{
				name:                  "Unsupported kind resource referenced as CertificateRef in a Gateway listener",
				gatewayNamespacedName: types.NamespacedName{Name: "gateway-certificate-unsupported-kind", Namespace: "gateway-conformance-infra"},
			},
			{
				name:                  "Malformed secret referenced as CertificateRef in a Gateway listener",
				gatewayNamespacedName: types.NamespacedName{Name: "gateway-certificate-malformed-secret", Namespace: "gateway-conformance-infra"},
			},
		}

		for _, tc := range testCases {
			tc := tc
			t.Run(tc.name, func(t *testing.T) {
				t.Parallel()
				kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, tc.gatewayNamespacedName, listeners)
			})
		}
	},
}
View Source
var GatewayModifyListeners = suite.ConformanceTest{
	ShortName:   "GatewayModifyListeners",
	Description: "A Gateway in the gateway-conformance-infra namespace should handle adding and removing listeners.",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
	},
	Manifests: []string{"tests/gateway-modify-listeners.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		t.Run("should be able to add a listener that then becomes available for routing traffic", func(t *testing.T) {
			gwNN := types.NamespacedName{Name: "gateway-add-listener", Namespace: "gateway-conformance-infra"}
			ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
			defer cancel()

			namespaces := []string{"gateway-conformance-infra"}
			kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces)

			kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN)

			original := &v1beta1.Gateway{}
			err := s.Client.Get(ctx, gwNN, original)
			require.NoErrorf(t, err, "error getting Gateway: %v", err)

			all := v1beta1.NamespacesFromAll

			mutate := original.DeepCopy()

			hostname := v1beta1.Hostname("data.test.com")
			mutate.Spec.Listeners = append(mutate.Spec.Listeners, v1beta1.Listener{
				Name:     "http",
				Port:     80,
				Protocol: v1beta1.HTTPProtocolType,
				Hostname: &hostname,
				AllowedRoutes: &v1beta1.AllowedRoutes{
					Namespaces: &v1beta1.RouteNamespaces{From: &all},
				},
			})

			err = s.Client.Patch(ctx, mutate, client.MergeFrom(original))
			require.NoErrorf(t, err, "error patching the Gateway: %v", err)

			kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces)

			listeners := []v1beta1.ListenerStatus{
				{
					Name: v1beta1.SectionName("https"),
					SupportedKinds: []v1beta1.RouteGroupKind{{
						Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
						Kind:  v1beta1.Kind("HTTPRoute"),
					}},
					Conditions: []metav1.Condition{{
						Type:   string(v1beta1.ListenerConditionAccepted),
						Status: metav1.ConditionTrue,
						Reason: "",
					}},
					AttachedRoutes: 1,
				},
				{
					Name: v1beta1.SectionName("http"),
					SupportedKinds: []v1beta1.RouteGroupKind{{
						Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
						Kind:  v1beta1.Kind("HTTPRoute"),
					}},
					Conditions: []metav1.Condition{{
						Type:   string(v1beta1.ListenerConditionAccepted),
						Status: metav1.ConditionTrue,
						Reason: "",
					}},
					AttachedRoutes: 1,
				},
			}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)

			kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN)

			updated := &v1beta1.Gateway{}
			err = s.Client.Get(ctx, gwNN, updated)
			require.NoErrorf(t, err, "error getting Gateway: %v", err)

			require.NotEqual(t, original.Generation, updated.Generation, "generation should change after an update")
		})

		t.Run("should be able to remove listeners, which would then stop routing the relevant traffic", func(t *testing.T) {
			gwNN := types.NamespacedName{Name: "gateway-remove-listener", Namespace: "gateway-conformance-infra"}
			ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
			defer cancel()

			namespaces := []string{"gateway-conformance-infra"}
			kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces)

			kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN)

			original := &v1beta1.Gateway{}
			err := s.Client.Get(ctx, gwNN, original)
			require.NoErrorf(t, err, "error getting Gateway: %v", err)

			mutate := original.DeepCopy()
			require.Equalf(t, 2, len(mutate.Spec.Listeners), "the gateway must have 2 listeners")

			// remove the "https" Gateway listener, leaving only the "http" listener
			var newListeners []v1beta1.Listener
			for _, listener := range mutate.Spec.Listeners {
				if listener.Name == "http" {
					newListeners = append(newListeners, listener)
				}
			}
			mutate.Spec.Listeners = newListeners

			err = s.Client.Patch(ctx, mutate, client.MergeFrom(original))
			require.NoErrorf(t, err, "error patching the Gateway: %v", err)

			kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces)

			listeners := []v1beta1.ListenerStatus{
				{
					Name: v1beta1.SectionName("http"),
					SupportedKinds: []v1beta1.RouteGroupKind{{
						Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
						Kind:  v1beta1.Kind("HTTPRoute"),
					}},
					Conditions: []metav1.Condition{{
						Type:   string(v1beta1.ListenerConditionAccepted),
						Status: metav1.ConditionTrue,
						Reason: "",
					}},
					AttachedRoutes: 1,
				},
			}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)

			kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN)

			updated := &v1beta1.Gateway{}
			err = s.Client.Get(ctx, gwNN, updated)
			require.NoErrorf(t, err, "error getting Gateway: %v", err)

			require.NotEqual(t, original.Generation, updated.Generation, "generation should change after an update")
		})
	},
}
View Source
var GatewayObservedGenerationBump = suite.ConformanceTest{
	ShortName:   "GatewayObservedGenerationBump",
	Description: "A Gateway in the gateway-conformance-infra namespace should update the observedGeneration in all of its Status.Conditions after an update to the spec",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportGatewayPort8080,
	},
	Manifests: []string{"tests/gateway-observed-generation-bump.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		gwNN := types.NamespacedName{Name: "gateway-observed-generation-bump", Namespace: "gateway-conformance-infra"}

		t.Run("observedGeneration should increment", func(t *testing.T) {
			ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
			defer cancel()

			namespaces := []string{"gateway-conformance-infra"}
			kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces)

			kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN)

			original := &v1beta1.Gateway{}
			err := s.Client.Get(ctx, gwNN, original)
			require.NoErrorf(t, err, "error getting Gateway: %v", err)

			all := v1beta1.NamespacesFromAll

			mutate := original.DeepCopy()

			mutate.Spec.Listeners = append(mutate.Spec.Listeners, v1beta1.Listener{
				Name:     "alternate",
				Port:     8080,
				Protocol: v1beta1.HTTPProtocolType,
				AllowedRoutes: &v1beta1.AllowedRoutes{
					Namespaces: &v1beta1.RouteNamespaces{From: &all},
				},
			})

			err = s.Client.Patch(ctx, mutate, client.MergeFrom(original))
			require.NoErrorf(t, err, "error patching the Gateway: %v", err)

			kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces)

			kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN)

			updated := &v1beta1.Gateway{}
			err = s.Client.Get(ctx, gwNN, updated)
			require.NoErrorf(t, err, "error getting Gateway: %v", err)

			require.NotEqual(t, original.Generation, updated.Generation, "generation should change after an update")
		})
	},
}
View Source
var GatewaySecretInvalidReferenceGrant = suite.ConformanceTest{
	ShortName:   "GatewaySecretInvalidReferenceGrant",
	Description: "A Gateway in the gateway-conformance-infra namespace should fail to become ready if the Gateway has a certificateRef for a Secret in the gateway-conformance-web-backend namespace and a ReferenceGrant exists but does not grant permission to that specific Secret",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportReferenceGrant,
	},
	Manifests: []string{"tests/gateway-secret-invalid-reference-grant.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		gwNN := types.NamespacedName{Name: "gateway-secret-invalid-reference-grant", Namespace: "gateway-conformance-infra"}

		t.Run("Gateway listener should have a false ResolvedRefs condition with reason RefNotPermitted", func(t *testing.T) {
			listeners := []v1beta1.ListenerStatus{{
				Name: v1beta1.SectionName("https"),
				SupportedKinds: []v1beta1.RouteGroupKind{{
					Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
					Kind:  v1beta1.Kind("HTTPRoute"),
				}},
				Conditions: []metav1.Condition{{
					Type:   string(v1beta1.ListenerConditionResolvedRefs),
					Status: metav1.ConditionFalse,
					Reason: string(v1beta1.ListenerReasonRefNotPermitted),
				}},
				AttachedRoutes: 0,
			}}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)
		})
	},
}
View Source
var GatewaySecretMissingReferenceGrant = suite.ConformanceTest{
	ShortName:   "GatewaySecretMissingReferenceGrant",
	Description: "A Gateway in the gateway-conformance-infra namespace should fail to become programmed if the Gateway has a certificateRef for a Secret in the gateway-conformance-web-backend namespace and a ReferenceGrant granting permission to the Secret does not exist",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportReferenceGrant,
	},
	Manifests: []string{"tests/gateway-secret-missing-reference-grant.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		gwNN := types.NamespacedName{Name: "gateway-secret-missing-reference-grant", Namespace: "gateway-conformance-infra"}

		t.Run("Gateway listener should have a false ResolvedRefs condition with reason RefNotPermitted", func(t *testing.T) {
			listeners := []v1beta1.ListenerStatus{{
				Name: v1beta1.SectionName("https"),
				SupportedKinds: []v1beta1.RouteGroupKind{{
					Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
					Kind:  v1beta1.Kind("HTTPRoute"),
				}},
				Conditions: []metav1.Condition{{
					Type:   string(v1beta1.ListenerConditionResolvedRefs),
					Status: metav1.ConditionFalse,
					Reason: string(v1beta1.ListenerReasonRefNotPermitted),
				}},
				AttachedRoutes: 0,
			}}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)
		})
	},
}
View Source
var GatewaySecretReferenceGrantAllInNamespace = suite.ConformanceTest{
	ShortName:   "GatewaySecretReferenceGrantAllInNamespace",
	Description: "A Gateway in the gateway-conformance-infra namespace should become programmed if the Gateway has a certificateRef for a Secret in the gateway-conformance-web-backend namespace and a ReferenceGrant granting permission to all Secrets in the namespace exists",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportReferenceGrant,
	},
	Manifests: []string{"tests/gateway-secret-reference-grant-all-in-namespace.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		gwNN := types.NamespacedName{Name: "gateway-secret-reference-grant-all-in-namespace", Namespace: "gateway-conformance-infra"}

		t.Run("Gateway listener should have a true ResolvedRefs condition and a true Programmed condition", func(t *testing.T) {
			listeners := []v1beta1.ListenerStatus{{
				Name: v1beta1.SectionName("https"),
				SupportedKinds: []v1beta1.RouteGroupKind{{
					Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
					Kind:  v1beta1.Kind("HTTPRoute"),
				}},
				Conditions: []metav1.Condition{
					{
						Type:   string(v1beta1.ListenerConditionProgrammed),
						Status: metav1.ConditionTrue,
						Reason: string(v1beta1.ListenerReasonProgrammed),
					},
				},
				AttachedRoutes: 0,
			}}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)
		})
	},
}
View Source
var GatewaySecretReferenceGrantSpecific = suite.ConformanceTest{
	ShortName:   "GatewaySecretReferenceGrantSpecific",
	Description: "A Gateway in the gateway-conformance-infra namespace should become programmed if the Gateway has a certificateRef for a Secret in the gateway-conformance-web-backend namespace and a ReferenceGrant granting permission to the specific Secret exists",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportReferenceGrant,
	},
	Manifests: []string{"tests/gateway-secret-reference-grant-specific.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		gwNN := types.NamespacedName{Name: "gateway-secret-reference-grant-specific", Namespace: "gateway-conformance-infra"}

		t.Run("Gateway listener should have a true ResolvedRefs condition and a true Programmed condition", func(t *testing.T) {
			listeners := []v1beta1.ListenerStatus{{
				Name: v1beta1.SectionName("https"),
				SupportedKinds: []v1beta1.RouteGroupKind{{
					Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
					Kind:  v1beta1.Kind("HTTPRoute"),
				}},
				Conditions: []metav1.Condition{
					{
						Type:   string(v1beta1.ListenerConditionProgrammed),
						Status: metav1.ConditionTrue,
						Reason: string(v1beta1.ListenerReasonProgrammed),
					},
				},
				AttachedRoutes: 0,
			}}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)
		})
	},
}
View Source
var GatewayWithAttachedRoutes = suite.ConformanceTest{
	ShortName:   "GatewayWithAttachedRoutes",
	Description: "A Gateway in the gateway-conformance-infra namespace should be attached to routes.",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
	},
	Manifests: []string{"tests/gateway-with-attached-routes.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		t.Run("Gateway listener should have one valid http routes attached", func(t *testing.T) {
			gwNN := types.NamespacedName{Name: "gateway-with-one-attached-route", Namespace: "gateway-conformance-infra"}
			listeners := []v1beta1.ListenerStatus{{
				Name: v1beta1.SectionName("http"),
				SupportedKinds: []v1beta1.RouteGroupKind{{
					Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
					Kind:  v1beta1.Kind("HTTPRoute"),
				}},
				Conditions: []metav1.Condition{{
					Type:   string(v1beta1.ListenerConditionAccepted),
					Status: metav1.ConditionTrue,
					Reason: "",
				}},
				AttachedRoutes: 1,
			}}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)
		})

		t.Run("Gateway listener should have two valid http routes attached", func(t *testing.T) {
			gwNN := types.NamespacedName{Name: "gateway-with-two-attached-routes", Namespace: "gateway-conformance-infra"}
			listeners := []v1beta1.ListenerStatus{{
				Name: v1beta1.SectionName("http"),
				SupportedKinds: []v1beta1.RouteGroupKind{{
					Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
					Kind:  v1beta1.Kind("HTTPRoute"),
				}},
				Conditions: []metav1.Condition{{
					Type:   string(v1beta1.ListenerConditionAccepted),
					Status: metav1.ConditionTrue,
					Reason: "",
				}},
				AttachedRoutes: 2,
			}}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)
		})

		t.Run("Gateway listener should have attached route by specifying the sectionName", func(t *testing.T) {
			gwNN := types.NamespacedName{Name: "gateway-with-two-listeners-and-one-attached-route", Namespace: "gateway-conformance-infra"}
			listeners := []v1beta1.ListenerStatus{
				{
					Name: v1beta1.SectionName("http-unattached"),
					SupportedKinds: []v1beta1.RouteGroupKind{{
						Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
						Kind:  v1beta1.Kind("HTTPRoute"),
					}},
					Conditions: []metav1.Condition{{
						Type:   string(v1beta1.ListenerConditionAccepted),
						Status: metav1.ConditionTrue,
						Reason: "",
					}},
					AttachedRoutes: 0,
				},
				{
					Name: v1beta1.SectionName("http"),
					SupportedKinds: []v1beta1.RouteGroupKind{{
						Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group),
						Kind:  v1beta1.Kind("HTTPRoute"),
					}},
					Conditions: []metav1.Condition{{
						Type:   string(v1beta1.ListenerConditionAccepted),
						Status: metav1.ConditionTrue,
						Reason: "",
					}},
					AttachedRoutes: 1,
				},
			}

			kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners)
		})
	},
}
View Source
var HTTPExactPathMatching = suite.ConformanceTest{
	ShortName:   "HTTPExactPathMatching",
	Description: "A single HTTPRoute with exact path matching for different backends",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-exact-path-matching.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "exact-matching", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{
			{
				Request:   http.Request{Path: "/one"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			}, {
				Request:   http.Request{Path: "/two"},
				Backend:   "infra-backend-v2",
				Namespace: ns,
			}, {
				Request:  http.Request{Path: "/"},
				Response: http.Response{StatusCode: 404},
			}, {
				Request:  http.Request{Path: "/one/example"},
				Response: http.Response{StatusCode: 404},
			}, {
				Request:  http.Request{Path: "/two/"},
				Response: http.Response{StatusCode: 404},
			}, {
				Request:  http.Request{Path: "/Two"},
				Response: http.Response{StatusCode: 404},
			},
		}

		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteCrossNamespace = suite.ConformanceTest{
	ShortName:   "HTTPRouteCrossNamespace",
	Description: "A single HTTPRoute in the gateway-conformance-web-backend namespace should attach to Gateway in another namespace",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-cross-namespace.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "cross-namespace", Namespace: "gateway-conformance-web-backend"}
		gwNN := types.NamespacedName{Name: "backend-namespaces", Namespace: "gateway-conformance-infra"}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		t.Run("Simple HTTP request should reach web-backend", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request:   http.Request{Path: "/"},
				Response:  http.Response{StatusCode: 200},
				Backend:   "web-backend",
				Namespace: "gateway-conformance-web-backend",
			})
		})
	},
}
View Source
var HTTPRouteDisallowedKind = suite.ConformanceTest{
	ShortName:   "HTTPRouteDisallowedKind",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace should fail to attach to a Gateway with no listeners that allow the HTTPRoute kind",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportTLSRoute,
	},
	Manifests: []string{"tests/httproute-disallowed-kind.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {

		kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{"gateway-conformance-infra"})

		routeNN := types.NamespacedName{Name: "disallowed-kind", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "tlsroutes-only", Namespace: "gateway-conformance-infra"}
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		t.Run("Route should not have been accepted with reason NotAllowedByListeners", func(t *testing.T) {
			kubernetes.HTTPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, metav1.Condition{
				Type:   string(v1beta1.RouteConditionAccepted),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonNotAllowedByListeners),
			})
		})
		t.Run("Route should not have Parents set in status", func(t *testing.T) {
			kubernetes.HTTPRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, routeNN)
		})
		t.Run("Gateway should have 0 Routes attached", func(t *testing.T) {
			kubernetes.GatewayMustHaveZeroRoutes(t, suite.Client, suite.TimeoutConfig, gwNN)
		})
	},
}
View Source
var HTTPRouteHeaderMatching = suite.ConformanceTest{
	ShortName:   "HTTPRouteHeaderMatching",
	Description: "A single HTTPRoute with header matching for different backends",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-header-matching.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "header-matching", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{{
			Request:   http.Request{Path: "/", Headers: map[string]string{"Version": "one"}},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/", Headers: map[string]string{"Version": "two"}},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/", Headers: map[string]string{"Version": "two", "Color": "orange"}},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/", Headers: map[string]string{"Version": "two", "Color": "blue"}},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:  http.Request{Path: "/", Headers: map[string]string{"Color": "orange"}},
			Response: http.Response{StatusCode: 404},
		}, {
			Request:  http.Request{Path: "/", Headers: map[string]string{"Some-Other-Header": "one"}},
			Response: http.Response{StatusCode: 404},
		}, {
			Request:   http.Request{Path: "/", Headers: map[string]string{"Color": "blue"}},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/", Headers: map[string]string{"Color": "green"}},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/", Headers: map[string]string{"Color": "red"}},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/", Headers: map[string]string{"Color": "yellow"}},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:  http.Request{Path: "/", Headers: map[string]string{"Color": "purple"}},
			Response: http.Response{StatusCode: 404},
		}}

		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteHostnameIntersection = suite.ConformanceTest{
	ShortName:   "HTTPRouteHostnameIntersection",
	Description: "HTTPRoutes should attach to listeners only if they have intersecting hostnames, and should accept requests only for the intersecting hostnames",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-hostname-intersection.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		gwNN := types.NamespacedName{Name: "httproute-hostname-intersection", Namespace: ns}

		kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{ns})

		t.Run("HTTPRoutes that do intersect with listener hostnames", func(t *testing.T) {
			routes := []types.NamespacedName{
				{Namespace: ns, Name: "specific-host-matches-listener-specific-host"},
				{Namespace: ns, Name: "specific-host-matches-listener-wildcard-host"},
				{Namespace: ns, Name: "wildcard-host-matches-listener-specific-host"},
				{Namespace: ns, Name: "wildcard-host-matches-listener-wildcard-host"},
			}
			gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routes...)
			for _, routeNN := range routes {
				kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)
			}

			var testCases []http.ExpectedResponse

			testCases = append(testCases,
				http.ExpectedResponse{
					Request:   http.Request{Host: "very.specific.com", Path: "/s1"},
					Backend:   "infra-backend-v1",
					Namespace: ns,
				},

				http.ExpectedResponse{
					Request:   http.Request{Host: "very.specific.com:1234", Path: "/s1"},
					Backend:   "infra-backend-v1",
					Namespace: ns,
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "non.matching.com", Path: "/s1"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "foo.nonmatchingwildcard.io", Path: "/s1"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "foo.wildcard.io", Path: "/s1"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "very.specific.com", Path: "/non-matching-prefix"},
					Response: http.Response{StatusCode: 404},
				},
			)

			testCases = append(testCases,
				http.ExpectedResponse{
					Request:   http.Request{Host: "foo.wildcard.io", Path: "/s2"},
					Backend:   "infra-backend-v2",
					Namespace: ns,
				},
				http.ExpectedResponse{
					Request:   http.Request{Host: "bar.wildcard.io", Path: "/s2"},
					Backend:   "infra-backend-v2",
					Namespace: ns,
				},
				http.ExpectedResponse{
					Request:   http.Request{Host: "foo.bar.wildcard.io", Path: "/s2"},
					Backend:   "infra-backend-v2",
					Namespace: ns,
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "non.matching.com", Path: "/s2"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "wildcard.io", Path: "/s2"},
					Response: http.Response{StatusCode: 404},
				},

				http.ExpectedResponse{
					Request:  http.Request{Host: "very.specific.com", Path: "/s2"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "foo.wildcard.io", Path: "/non-matching-prefix"},
					Response: http.Response{StatusCode: 404},
				},
			)

			testCases = append(testCases,
				http.ExpectedResponse{
					Request:   http.Request{Host: "very.specific.com", Path: "/s3"},
					Backend:   "infra-backend-v3",
					Namespace: ns,
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "non.matching.com", Path: "/s3"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "foo.specific.com", Path: "/s3"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "foo.wildcard.io", Path: "/s3"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "very.specific.com", Path: "/non-matching-prefix"},
					Response: http.Response{StatusCode: 404},
				},
			)

			testCases = append(testCases,
				http.ExpectedResponse{
					Request:   http.Request{Host: "foo.anotherwildcard.io", Path: "/s4"},
					Backend:   "infra-backend-v1",
					Namespace: ns,
				},
				http.ExpectedResponse{
					Request:   http.Request{Host: "bar.anotherwildcard.io", Path: "/s4"},
					Backend:   "infra-backend-v1",
					Namespace: ns,
				},
				http.ExpectedResponse{
					Request:   http.Request{Host: "foo.bar.anotherwildcard.io", Path: "/s4"},
					Backend:   "infra-backend-v1",
					Namespace: ns,
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "anotherwildcard.io", Path: "/s4"},
					Response: http.Response{StatusCode: 404},
				},

				http.ExpectedResponse{
					Request:  http.Request{Host: "foo.wildcard.io", Path: "/s4"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "very.specific.com", Path: "/s4"},
					Response: http.Response{StatusCode: 404},
				},
				http.ExpectedResponse{
					Request:  http.Request{Host: "foo.anotherwildcard.io", Path: "/non-matching-prefix"},
					Response: http.Response{StatusCode: 404},
				},
			)

			for i := range testCases {

				tc := testCases[i]
				t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
					t.Parallel()
					http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
				})
			}
		})

		t.Run("HTTPRoutes that do not intersect with listener hostnames", func(t *testing.T) {
			gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN))
			routeNN := types.NamespacedName{Namespace: ns, Name: "no-intersecting-hosts"}

			parents := []v1beta1.RouteParentStatus{{
				ParentRef:      parentRefTo(gwNN),
				ControllerName: v1beta1.GatewayController(suite.ControllerName),
				Conditions: []metav1.Condition{
					{
						Type:   string(v1beta1.RouteConditionAccepted),
						Status: metav1.ConditionFalse,
						Reason: string(v1beta1.RouteReasonNoMatchingListenerHostname),
					},
				},
			}}

			kubernetes.HTTPRouteMustHaveParents(t, suite.Client, suite.TimeoutConfig, routeNN, parents, true)

			testCases := []http.ExpectedResponse{
				{
					Request:  http.Request{Host: "specific.but.wrong.com", Path: "/s5"},
					Response: http.Response{StatusCode: 404},
				},
				{
					Request:  http.Request{Host: "wildcard.io", Path: "/s5"},
					Response: http.Response{StatusCode: 404},
				},
			}

			for i := range testCases {

				tc := testCases[i]
				t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
					t.Parallel()
					http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
				})
			}
		})
	},
}
View Source
var HTTPRouteInvalidBackendRefUnknownKind = suite.ConformanceTest{
	ShortName:   "HTTPRouteInvalidBackendRefUnknownKind",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace should set a ResolvedRefs status False with reason InvalidKind when attempting to bind to a Gateway in the same namespace if the route has a BackendRef that points to an unknown Kind.",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-invalid-backendref-unknown-kind.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "invalid-backend-ref-unknown-kind", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}

		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

		t.Run("HTTPRoute with Invalid Kind has a ResolvedRefs Condition with status False and Reason InvalidKind", func(t *testing.T) {
			resolvedRefsCond := metav1.Condition{
				Type:   string(v1beta1.RouteConditionResolvedRefs),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonInvalidKind),
			}

			kubernetes.HTTPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
		})

		t.Run("HTTP Request to invalid backend with invalid Kind receives a 500", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request: http.Request{
					Method: "GET",
					Path:   "/v2",
				},
				Response: http.Response{StatusCode: 500},
			})
		})
	},
}
View Source
var HTTPRouteInvalidCrossNamespaceBackendRef = suite.ConformanceTest{
	ShortName:   "HTTPRouteInvalidCrossNamespaceBackendRef",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace should set a ResolvedRefs status False with reason RefNotPermitted when attempting to bind to a Gateway in the same namespace if the route has a BackendRef Service in the gateway-conformance-web-backend namespace and a ReferenceGrant granting permission to route to that Service does not exist",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportReferenceGrant,
	},
	Manifests: []string{"tests/httproute-invalid-cross-namespace-backend-ref.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "invalid-cross-namespace-backend-ref", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}

		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

		t.Run("HTTPRoute with a cross-namespace BackendRef and no ReferenceGrant has a ResolvedRefs Condition with status False and Reason RefNotPermitted", func(t *testing.T) {
			resolvedRefsCond := metav1.Condition{
				Type:   string(v1beta1.RouteConditionResolvedRefs),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonRefNotPermitted),
			}

			kubernetes.HTTPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
		})

		t.Run("HTTP Request to invalid cross-namespace backend must receive a 500", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request: http.Request{
					Method: "GET",
					Path:   "/",
				},
				Response: http.Response{StatusCode: 500},
			})
		})
	},
}
View Source
var HTTPRouteInvalidCrossNamespaceParentRef = suite.ConformanceTest{
	ShortName:   "HTTPRouteInvalidCrossNamespaceParentRef",
	Description: "A single HTTPRoute in the gateway-conformance-web-backend namespace should fail to attach to a Gateway in another namespace that it is not allowed to",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-invalid-cross-namespace-parent-ref.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}
		routeNN := types.NamespacedName{Name: "invalid-cross-namespace-parent-ref", Namespace: "gateway-conformance-web-backend"}
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		t.Run("HTTPRoute should have an Accepted: false condition with reason NotAllowedByListeners", func(t *testing.T) {
			acceptedCond := metav1.Condition{
				Type:   string(v1beta1.RouteConditionAccepted),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonNotAllowedByListeners),
			}

			kubernetes.HTTPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, acceptedCond)
		})

		t.Run("Route should not have Parents set in status", func(t *testing.T) {
			kubernetes.HTTPRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, routeNN)
		})

		t.Run("Gateway should have 0 Routes attached", func(t *testing.T) {
			kubernetes.GatewayMustHaveZeroRoutes(t, suite.Client, suite.TimeoutConfig, gwNN)
		})
	},
}
View Source
var HTTPRouteInvalidNonExistentBackendRef = suite.ConformanceTest{
	ShortName:   "HTTPRouteInvalidNonExistentBackendRef",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace should set a ResolvedRefs status False with reason BackendNotFound and return 500 when binding to a Gateway in the same namespace if the route has a BackendRef Service that does not exist",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-invalid-backendref-nonexistent.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "invalid-nonexistent-backend-ref", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}

		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

		t.Run("HTTPRoute with only a nonexistent BackendRef has a ResolvedRefs Condition with status False and Reason BackendNotFound", func(t *testing.T) {
			resolvedRefsCond := metav1.Condition{
				Type:   string(v1beta1.RouteConditionResolvedRefs),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonBackendNotFound),
			}

			kubernetes.HTTPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
		})

		t.Run("HTTP Request to invalid nonexistent backend receive a 500", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request: http.Request{
					Method: "GET",
					Path:   "/",
				},
				Response: http.Response{StatusCode: 500},
			})
		})
	},
}
View Source
var HTTPRouteInvalidParentRefNotMatchingListenerPort = suite.ConformanceTest{
	ShortName:   "HTTPRouteInvalidParentRefNotMatchingListenerPort",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace should set the Accepted status to False with reason NoMatchingParent when attempting to bind to a Gateway that does not have a matching ListenerPort.",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportRouteDestinationPortMatching,
	},
	Manifests: []string{"tests/httproute-invalid-parentref-not-matching-listener-port.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "httproute-listener-not-matching-route-port", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		t.Run("HTTPRoute with no matching port in ParentRef has an Accepted Condition with status False and Reason NoMatchingParent", func(t *testing.T) {
			acceptedCond := metav1.Condition{
				Type:   string(v1beta1.RouteConditionAccepted),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonNoMatchingParent),
			}

			kubernetes.HTTPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, acceptedCond)
		})

		t.Run("Route should not have Parents accepted in status", func(t *testing.T) {
			kubernetes.HTTPRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, routeNN)
		})

		t.Run("Gateway should have 0 Routes attached", func(t *testing.T) {
			kubernetes.GatewayMustHaveZeroRoutes(t, suite.Client, suite.TimeoutConfig, gwNN)
		})
	},
}
View Source
var HTTPRouteInvalidParentRefNotMatchingSectionName = suite.ConformanceTest{
	ShortName:   "HTTPRouteInvalidParentRefNotMatchingSectionName",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace should set the Accepted status to False with reason NoMatchingParent when attempting to bind to a Gateway that does not have a matching SectionName.",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-invalid-parentref-not-matching-section-name.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "httproute-listener-not-matching-section-name", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}

		t.Run("HTTPRoute with no matching sectionName in ParentRef has an Accepted Condition with status False and Reason NoMatchingParent", func(t *testing.T) {
			resolvedRefsCond := metav1.Condition{
				Type:   string(v1beta1.RouteConditionAccepted),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonNoMatchingParent),
			}

			kubernetes.HTTPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
		})

		t.Run("Route should not have Parents accepted in status", func(t *testing.T) {
			kubernetes.HTTPRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, routeNN)
		})

		t.Run("Gateway should have 0 Routes attached", func(t *testing.T) {
			kubernetes.GatewayMustHaveZeroRoutes(t, suite.Client, suite.TimeoutConfig, gwNN)
		})
	},
}
View Source
var HTTPRouteInvalidReferenceGrant = suite.ConformanceTest{
	ShortName:   "HTTPRouteInvalidReferenceGrant",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace, with a backendRef in another namespace without valid ReferenceGrant, should have the ResolvedRefs condition set to False and not forward HTTP requests to any backend",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportReferenceGrant,
	},
	Manifests: []string{"tests/httproute-invalid-reference-grant.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "reference-grant", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

		t.Run("HTTPRoute with BackendRef in another namespace and no ReferenceGrant covering the Service has a ResolvedRefs Condition with status False and Reason RefNotPermitted", func(t *testing.T) {
			resolvedRefsCond := metav1.Condition{
				Type:   string(v1beta1.RouteConditionResolvedRefs),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonRefNotPermitted),
			}

			kubernetes.HTTPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
		})

		t.Run("Simple HTTP request not should reach web-backend", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request: http.Request{
					Method: "GET",
					Path:   "/",
				},
				Response:  http.Response{StatusCode: 500},
				Backend:   "web-backend",
				Namespace: "gateway-conformance-web-backend",
			})
		})
	},
}
View Source
var HTTPRouteListenerHostnameMatching = suite.ConformanceTest{
	ShortName:   "HTTPRouteListenerHostnameMatching",
	Description: "Multiple HTTP listeners with the same port and different hostnames, each with a different HTTPRoute",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-listener-hostname-matching.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"

		kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{ns})

		routeNN1 := types.NamespacedName{Name: "backend-v1", Namespace: ns}
		routeNN2 := types.NamespacedName{Name: "backend-v2", Namespace: ns}
		routeNN3 := types.NamespacedName{Name: "backend-v3", Namespace: ns}
		gwNN := types.NamespacedName{Name: "httproute-listener-hostname-matching", Namespace: ns}

		kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN, "listener-1"), routeNN1)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN1, gwNN)

		kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN, "listener-2"), routeNN2)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN2, gwNN)

		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN, "listener-3", "listener-4"), routeNN3)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN3, gwNN)

		testCases := []http.ExpectedResponse{{
			Request:   http.Request{Host: "bar.com", Path: "/"},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Host: "foo.bar.com", Path: "/"},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:   http.Request{Host: "baz.bar.com", Path: "/"},
			Backend:   "infra-backend-v3",
			Namespace: ns,
		}, {
			Request:   http.Request{Host: "boo.bar.com", Path: "/"},
			Backend:   "infra-backend-v3",
			Namespace: ns,
		}, {
			Request:   http.Request{Host: "multiple.prefixes.bar.com", Path: "/"},
			Backend:   "infra-backend-v3",
			Namespace: ns,
		}, {
			Request:   http.Request{Host: "multiple.prefixes.foo.com", Path: "/"},
			Backend:   "infra-backend-v3",
			Namespace: ns,
		}, {
			Request:  http.Request{Host: "foo.com", Path: "/"},
			Response: http.Response{StatusCode: 404},
		}, {
			Request:  http.Request{Host: "no.matching.host", Path: "/"},
			Response: http.Response{StatusCode: 404},
		}}

		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteMatching = suite.ConformanceTest{
	ShortName:   "HTTPRouteMatching",
	Description: "A single HTTPRoute with path and header matching for different backends",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-matching.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "matching", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{{
			Request:   http.Request{Path: "/"},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/example"},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/", Headers: map[string]string{"Version": "one"}},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/v2"},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/v2/example"},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/", Headers: map[string]string{"Version": "two"}},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/v2/"},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {

			Request:   http.Request{Path: "/v2example"},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/foo/v2/example"},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}}

		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteMatchingAcrossRoutes = suite.ConformanceTest{
	ShortName:   "HTTPRouteMatchingAcrossRoutes",
	Description: "Two HTTPRoutes with path matching for different backends",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-matching-across-routes.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN1 := types.NamespacedName{Name: "matching-part1", Namespace: ns}
		routeNN2 := types.NamespacedName{Name: "matching-part2", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN1, routeNN2)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN1, gwNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN2, gwNN)

		testCases := []http.ExpectedResponse{{
			Request: http.Request{
				Host: "example.com",
				Path: "/",
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Host: "example.com",
				Path: "/example",
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Host: "example.net",
				Path: "/example",
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Host:    "example.com",
				Path:    "/example",
				Headers: map[string]string{"Version": "one"},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Host: "example.com",
				Path: "/v2",
			},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request: http.Request{

				Host: "example.net",
				Path: "/v2",
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Host: "example.com",
				Path: "/v2/example",
			},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request: http.Request{
				Host:    "example.com",
				Path:    "/",
				Headers: map[string]string{"Version": "two"},
			},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}}

		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteMethodMatching = suite.ConformanceTest{
	ShortName:   "HTTPRouteMethodMatching",
	Description: "A single HTTPRoute with method matching for different backends",
	Manifests:   []string{"tests/httproute-method-matching.yaml"},
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPRouteMethodMatching,
	},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "method-matching", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{
			{
				Request:   http.Request{Method: "POST", Path: "/"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			}, {
				Request:   http.Request{Method: "GET", Path: "/"},
				Backend:   "infra-backend-v2",
				Namespace: ns,
			}, {
				Request:  http.Request{Method: "HEAD", Path: "/"},
				Response: http.Response{StatusCode: 404},
			},
		}

		testCases = append(testCases, []http.ExpectedResponse{
			{
				Request:   http.Request{Path: "/path1", Method: "GET"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
			{
				Request:   http.Request{Headers: map[string]string{"version": "one"}, Path: "/", Method: "PUT"},
				Backend:   "infra-backend-v2",
				Namespace: ns,
			},
			{
				Request:   http.Request{Headers: map[string]string{"version": "two"}, Path: "/path2", Method: "POST"},
				Backend:   "infra-backend-v3",
				Namespace: ns,
			},
		}...)

		testCases = append(testCases, []http.ExpectedResponse{
			{
				Request:   http.Request{Path: "/path3", Method: "PATCH"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
			{
				Request:   http.Request{Headers: map[string]string{"version": "three"}, Path: "/path4", Method: "DELETE"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
		}...)

		testCases = append(testCases, []http.ExpectedResponse{
			{
				Request:  http.Request{Path: "/", Method: "PUT"},
				Response: http.Response{StatusCode: 404},
			},
			{
				Request:  http.Request{Path: "/path4", Method: "DELETE"},
				Response: http.Response{StatusCode: 404},
			},
		}...)

		testCases = append(testCases, []http.ExpectedResponse{
			{
				Request:   http.Request{Path: "/path5", Method: "PATCH"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
			{
				Request:   http.Request{Headers: map[string]string{"version": "four"}, Path: "/", Method: "PATCH"},
				Backend:   "infra-backend-v2",
				Namespace: ns,
			},
		}...)

		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteObservedGenerationBump = suite.ConformanceTest{
	ShortName:   "HTTPRouteObservedGenerationBump",
	Description: "A HTTPRoute in the gateway-conformance-infra namespace should update the observedGeneration in all of it's Status.Conditions after an update to the spec",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-observed-generation-bump.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "observed-generation-bump", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}

		t.Run("observedGeneration should increment", func(t *testing.T) {
			ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
			defer cancel()

			namespaces := []string{"gateway-conformance-infra"}
			kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, namespaces)

			original := &v1beta1.HTTPRoute{}
			err := suite.Client.Get(ctx, routeNN, original)
			require.NoErrorf(t, err, "error getting HTTPRoute: %v", err)

			kubernetes.HTTPRouteMustHaveLatestConditions(t, original)

			mutate := original.DeepCopy()
			mutate.Spec.Rules[0].BackendRefs[0].Name = "infra-backend-v2"
			err = suite.Client.Patch(ctx, mutate, client.MergeFrom(original))
			require.NoErrorf(t, err, "error patching the HTTPRoute: %v", err)

			kubernetes.HTTPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, metav1.Condition{
				Type:   string(v1beta1.RouteConditionAccepted),
				Status: metav1.ConditionTrue,
				Reason: "",
			})
			kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

			updated := &v1beta1.HTTPRoute{}
			err = suite.Client.Get(ctx, routeNN, updated)
			require.NoErrorf(t, err, "error getting Gateway: %v", err)

			kubernetes.HTTPRouteMustHaveLatestConditions(t, updated)

			require.NotEqual(t, original.Generation, updated.Generation, "generation should change after an update")
		})
	},
}
View Source
var HTTPRoutePartiallyInvalidViaInvalidReferenceGrant = suite.ConformanceTest{
	ShortName:   "HTTPRoutePartiallyInvalidViaInvalidReferenceGrant",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace should attach to a Gateway in the same namespace if the route has a backendRef Service in the gateway-conformance-app-backend namespace and a ReferenceGrant exists but does not grant permission to route to that specific Service",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportReferenceGrant,
	},
	Manifests: []string{"tests/httproute-partially-invalid-via-reference-grant.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "invalid-reference-grant", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}

		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, s.Client, s.TimeoutConfig, s.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

		t.Run("HTTPRoute with BackendRef in another namespace and no ReferenceGrant covering the Service has a ResolvedRefs Condition with status False and Reason RefNotPermitted", func(t *testing.T) {
			resolvedRefsCond := metav1.Condition{
				Type:   string(v1beta1.RouteConditionResolvedRefs),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonRefNotPermitted),
			}

			kubernetes.HTTPRouteMustHaveCondition(t, s.Client, s.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
		})

		t.Run("HTTP Request to invalid backend with missing referenceGrant should receive a 500", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, s.RoundTripper, s.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request: http.Request{
					Method: "GET",
					Path:   "/v2",
				},
				Response: http.Response{StatusCode: 500},
			})
		})

		t.Run("HTTP Request to valid sibling backend should succeed", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, s.RoundTripper, s.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request: http.Request{
					Method: "GET",
					Path:   "/",
				},
				Response:  http.Response{StatusCode: 200},
				Backend:   "app-backend-v1",
				Namespace: "gateway-conformance-app-backend",
			})
		})
	},
}
View Source
var HTTPRoutePathMatchOrder = suite.ConformanceTest{
	ShortName:   "HTTPRoutePathMatchOrder",
	Description: "An HTTPRoute where there are multiple matches routing to any given backend follows match order precedence",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-path-match-order.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Namespace: ns, Name: "path-matching-order"}
		gwNN := types.NamespacedName{Namespace: ns, Name: "same-namespace"}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{
			{
				Request:   http.Request{Path: "/match/exact/one"},
				Backend:   "infra-backend-v3",
				Namespace: ns,
			}, {
				Request:   http.Request{Path: "/match/exact"},
				Backend:   "infra-backend-v2",
				Namespace: ns,
			}, {
				Request:   http.Request{Path: "/match"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			}, {
				Request:   http.Request{Path: "/match/prefix/one/any"},
				Backend:   "infra-backend-v2",
				Namespace: ns,
			}, {
				Request:   http.Request{Path: "/match/prefix/any"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			}, {
				Request:   http.Request{Path: "/match/any"},
				Backend:   "infra-backend-v3",
				Namespace: ns,
			},
		}

		for i := range testCases {
			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteQueryParamMatching = suite.ConformanceTest{
	ShortName:   "HTTPRouteQueryParamMatching",
	Description: "A single HTTPRoute with query param matching for different backends",
	Manifests:   []string{"tests/httproute-query-param-matching.yaml"},
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPRouteQueryParamMatching,
	},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Namespace: ns, Name: "query-param-matching"}
		gwNN := types.NamespacedName{Namespace: ns, Name: "same-namespace"}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{{
			Request:   http.Request{Path: "/?animal=whale"},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/?animal=dolphin"},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/?animal=dolphin&color=blue"},
			Backend:   "infra-backend-v3",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/?ANIMAL=Whale"},
			Backend:   "infra-backend-v3",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/?animal=whale&otherparam=irrelevant"},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request:   http.Request{Path: "/?animal=dolphin&color=yellow"},
			Backend:   "infra-backend-v2",
			Namespace: ns,
		}, {
			Request:  http.Request{Path: "/?color=blue"},
			Response: http.Response{StatusCode: 404},
		}, {
			Request:  http.Request{Path: "/?animal=dog"},
			Response: http.Response{StatusCode: 404},
		}, {
			Request:  http.Request{Path: "/?animal=whaledolphin"},
			Response: http.Response{StatusCode: 404},
		}, {
			Request:  http.Request{Path: "/"},
			Response: http.Response{StatusCode: 404},
		}}

		testCases = append(testCases, []http.ExpectedResponse{
			{
				Request:   http.Request{Path: "/path1?animal=whale"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
			{
				Request:   http.Request{Headers: map[string]string{"version": "one"}, Path: "/?animal=whale"},
				Backend:   "infra-backend-v2",
				Namespace: ns,
			},
			{
				Request:   http.Request{Headers: map[string]string{"version": "two"}, Path: "/path2?animal=whale"},
				Backend:   "infra-backend-v3",
				Namespace: ns,
			},
		}...)

		testCases = append(testCases, []http.ExpectedResponse{
			{
				Request:   http.Request{Path: "/path3?animal=shark"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
			{
				Request:   http.Request{Headers: map[string]string{"version": "three"}, Path: "/path4?animal=kraken"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
		}...)

		testCases = append(testCases, []http.ExpectedResponse{
			{
				Request:  http.Request{Path: "/?animal=shark"},
				Response: http.Response{StatusCode: 404},
			},
			{
				Request:  http.Request{Path: "/path4?animal=kraken"},
				Response: http.Response{StatusCode: 404},
			},
		}...)

		testCases = append(testCases, []http.ExpectedResponse{
			{
				Request:   http.Request{Path: "/path5?animal=hydra"},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
			{
				Request:   http.Request{Headers: map[string]string{"version": "four"}, Path: "/?animal=hydra"},
				Backend:   "infra-backend-v3",
				Namespace: ns,
			},
		}...)

		for i := range testCases {
			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteRedirectHostAndStatus = suite.ConformanceTest{
	ShortName:   "HTTPRouteRedirectHostAndStatus",
	Description: "An HTTPRoute with hostname and statusCode redirect filters",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-redirect-host-and-status.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "redirect-host-and-status", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{
			{
				Request: http.Request{
					Path:             "/hostname-redirect",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Host: "example.org",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/host-and-status",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 301,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Host: "example.org",
				},
				Namespace: ns,
			},
		}
		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteRedirectPath = suite.ConformanceTest{
	ShortName:   "HTTPRouteRedirectPath",
	Description: "An HTTPRoute with scheme redirect filter",
	Manifests:   []string{"tests/httproute-redirect-path.yaml"},
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPRoutePathRedirect,
	},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "redirect-path", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{
			{
				Request: http.Request{
					Path:             "/original-prefix/lemon",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Path: "/replacement-prefix/lemon",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/full/path/original",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Path: "/full-path-replacement",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/path-and-host",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Host: "example.org",
					Path: "/replacement-prefix",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/path-and-status",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 301,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Path: "/replacement-prefix",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/full-path-and-host",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Host: "example.org",
					Path: "/replacement-full",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/full-path-and-status",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 301,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Path: "/replacement-full",
				},
				Namespace: ns,
			},
		}
		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteRedirectPort = suite.ConformanceTest{
	ShortName:   "HTTPRouteRedirectPort",
	Description: "An HTTPRoute with a port redirect filter",
	Manifests:   []string{"tests/httproute-redirect-port.yaml"},
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPRoutePortRedirect,
	},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "redirect-port", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{
			{
				Request: http.Request{
					Path:             "/port",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Port: "8083",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/port-and-host",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Host: "example.org",
					Port: "8083",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/port-and-status",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 301,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Port: "8083",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/port-and-host-and-status",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Port: "8083",
					Host: "example.org",
				},
				Namespace: ns,
			},
		}
		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteRedirectPortAndScheme = suite.ConformanceTest{
	ShortName:   "HTTPRouteRedirectPortAndScheme",
	Description: "An HTTPRoute with port and scheme redirect filter",
	Manifests:   []string{"tests/httproute-redirect-port-and-scheme.yaml"},
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPRoutePortRedirect,
		suite.SupportGatewayPort8080,
	},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"

		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		routeNN := types.NamespacedName{Name: "http-route-for-listener-on-port-80", Namespace: ns}
		gwAddr80 := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		gwNN = types.NamespacedName{Name: "same-namespace-with-http-listener-on-8080", Namespace: ns}
		routeNN = types.NamespacedName{Name: "http-route-for-listener-on-port-8080", Namespace: ns}
		gwAddr8080 := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		gwNN = types.NamespacedName{Name: "same-namespace-with-https-listener", Namespace: ns}
		routeNN = types.NamespacedName{Name: "http-route-for-listener-on-port-443", Namespace: ns}
		gwAddr443 := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		certNN := types.NamespacedName{Name: "tls-validity-checks-certificate", Namespace: ns}
		cPem, keyPem, err := GetTLSSecret(suite.Client, certNN)
		if err != nil {
			t.Fatalf("unexpected error finding TLS secret: %v", err)
		}

		testCases := []http.ExpectedResponse{
			{
				Request: http.Request{
					Path:             "/scheme-nil-and-port-nil",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "http",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path:             "/scheme-nil-and-port-80",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "http",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path:             "/scheme-nil-and-port-8080",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "http",
					Port:   "8080",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path:             "/scheme-https-and-port-nil",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path:             "/scheme-https-and-port-443",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path:             "/scheme-https-and-port-8443",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
					Port:   "8443",
					Host:   "example.org",
				},
				Namespace: ns,
			},
		}

		for i := range testCases {
			tc := testCases[i]
			t.Run("http-listener-on-80/"+tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr80, tc)
			})
		}

		testCases = []http.ExpectedResponse{
			{
				Request: http.Request{
					Path:             "/scheme-nil-and-port-nil",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "http",
					Port:   "8080",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path:             "/scheme-nil-and-port-80",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "http",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path:             "/scheme-https-and-port-nil",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
					Host:   "example.org",
				},
				Namespace: ns,
			},
		}

		for i := range testCases {
			tc := testCases[i]
			t.Run("http-listener-on-8080/"+tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr8080, tc)
			})
		}

		testCases = []http.ExpectedResponse{
			{
				Request: http.Request{
					Host:             "example.org",
					Path:             "/scheme-nil-and-port-nil",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Host:             "example.org",
					Path:             "/scheme-nil-and-port-443",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Host:             "example.org",
					Path:             "/scheme-nil-and-port-8443",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
					Port:   "8443",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Host:             "example.org",
					Path:             "/scheme-http-and-port-nil",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "http",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Host:             "example.org",
					Path:             "/scheme-http-and-port-80",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "http",
					Host:   "example.org",
				},
				Namespace: ns,
			},
			{
				Request: http.Request{
					Host:             "example.org",
					Path:             "/scheme-http-and-port-8080",
					UnfollowRedirect: true,
				},
				Response: http.Response{StatusCode: 302},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "http",
					Port:   "8080",
					Host:   "example.org",
				},
				Namespace: ns,
			},
		}

		for i := range testCases {
			tc := testCases[i]
			t.Run("https-listener-on-443/"+tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr443, cPem, keyPem, "example.org", tc)
			})
		}
	},
}
View Source
var HTTPRouteRedirectScheme = suite.ConformanceTest{
	ShortName:   "HTTPRouteRedirectScheme",
	Description: "An HTTPRoute with a scheme redirect filter",
	Manifests:   []string{"tests/httproute-redirect-scheme.yaml"},
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPRouteSchemeRedirect,
	},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "redirect-scheme", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{
			{
				Request: http.Request{
					Path:             "/scheme",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/scheme-and-host",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Host:   "example.org",
					Scheme: "https",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/scheme-and-status",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 301,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
				},
				Namespace: ns,
			}, {
				Request: http.Request{
					Path:             "/scheme-and-host-and-status",
					UnfollowRedirect: true,
				},
				Response: http.Response{
					StatusCode: 302,
				},
				RedirectRequest: &roundtripper.RedirectRequest{
					Scheme: "https",
					Host:   "example.org",
				},
				Namespace: ns,
			},
		}
		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteReferenceGrant = suite.ConformanceTest{
	ShortName:   "HTTPRouteReferenceGrant",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace, with a backendRef in the gateway-conformance-web-backend namespace, should attach to Gateway in the gateway-conformance-infra namespace",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportReferenceGrant,
	},
	Manifests: []string{"tests/httproute-reference-grant.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "reference-grant", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: "gateway-conformance-infra"}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		t.Run("Simple HTTP request should reach web-backend", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request: http.Request{
					Method: "GET",
					Path:   "/",
				},
				Response:  http.Response{StatusCode: 200},
				Backend:   "web-backend",
				Namespace: "gateway-conformance-web-backend",
			})
		})

		ctx, cancel := context.WithTimeout(context.Background(), suite.TimeoutConfig.DeleteTimeout)
		defer cancel()
		rg := v1beta1.ReferenceGrant{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "reference-grant",
				Namespace: "gateway-conformance-web-backend",
			},
		}
		require.NoError(t, suite.Client.Delete(ctx, &rg))

		t.Run("Simple HTTP request should return 500 after deleting the relevant reference grant", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request: http.Request{
					Method: "GET",
					Path:   "/",
				},
				Response: http.Response{StatusCode: 500},
			})
		})
	},
}
View Source
var HTTPRouteRequestHeaderModifier = suite.ConformanceTest{
	ShortName:   "HTTPRouteRequestHeaderModifier",
	Description: "An HTTPRoute has request header modifier filters applied correctly",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-request-header-modifier.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "request-header-modifier", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{{
			Request: http.Request{
				Path: "/set",
				Headers: map[string]string{
					"Some-Other-Header": "val",
				},
			},
			ExpectedRequest: &http.ExpectedRequest{
				Request: http.Request{
					Path: "/set",
					Headers: map[string]string{
						"Some-Other-Header": "val",
						"X-Header-Set":      "set-overwrites-values",
					},
				},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/set",
				Headers: map[string]string{
					"Some-Other-Header": "val",
					"X-Header-Set":      "some-other-value",
				},
			},
			ExpectedRequest: &http.ExpectedRequest{
				Request: http.Request{
					Path: "/set",
					Headers: map[string]string{
						"Some-Other-Header": "val",
						"X-Header-Set":      "set-overwrites-values",
					},
				},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/add",
				Headers: map[string]string{
					"Some-Other-Header": "val",
				},
			},
			ExpectedRequest: &http.ExpectedRequest{
				Request: http.Request{
					Path: "/add",
					Headers: map[string]string{
						"Some-Other-Header": "val",
						"X-Header-Add":      "add-appends-values",
					},
				},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/add",
				Headers: map[string]string{
					"Some-Other-Header": "val",
					"X-Header-Add":      "some-other-value",
				},
			},
			ExpectedRequest: &http.ExpectedRequest{
				Request: http.Request{
					Path: "/add",
					Headers: map[string]string{
						"Some-Other-Header": "val",
						"X-Header-Add":      "some-other-value,add-appends-values",
					},
				},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/remove",
				Headers: map[string]string{
					"X-Header-Remove": "val",
				},
			},
			ExpectedRequest: &http.ExpectedRequest{
				Request: http.Request{
					Path: "/remove",
				},
				AbsentHeaders: []string{"X-Header-Remove"},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/multiple",
				Headers: map[string]string{
					"X-Header-Set-2":    "set-val-2",
					"X-Header-Add-2":    "add-val-2",
					"X-Header-Remove-2": "remove-val-2",
					"Another-Header":    "another-header-val",
				},
			},
			ExpectedRequest: &http.ExpectedRequest{
				Request: http.Request{
					Path: "/multiple",
					Headers: map[string]string{
						"X-Header-Set-1": "header-set-1",
						"X-Header-Set-2": "header-set-2",
						"X-Header-Add-1": "header-add-1",
						"X-Header-Add-2": "add-val-2,header-add-2",
						"X-Header-Add-3": "header-add-3",
						"Another-Header": "another-header-val",
					},
				},
				AbsentHeaders: []string{"X-Header-Remove-1", "X-Header-Remove-2"},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/case-insensitivity",

				Headers: map[string]string{
					"x-header-set":    "original-val-set",
					"x-header-add":    "original-val-add",
					"x-header-remove": "original-val-remove",
					"Another-Header":  "another-header-val",
				},
			},
			ExpectedRequest: &http.ExpectedRequest{
				Request: http.Request{
					Path: "/case-insensitivity",
					Headers: map[string]string{
						"X-Header-Set":   "header-set",
						"X-Header-Add":   "original-val-add,header-add",
						"Another-Header": "another-header-val",
					},
				},
				AbsentHeaders: []string{"x-header-remove", "X-Header-Remove"},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}}

		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteRequestMirror = suite.ConformanceTest{
	ShortName:   "HTTPRouteRequestMirror",
	Description: "An HTTPRoute with request mirror filter",
	Manifests:   []string{"tests/httproute-request-mirror.yaml"},
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPRouteRequestMirror,
	},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "request-mirror", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

		testCases := []http.ExpectedResponse{
			{
				Request: http.Request{
					Path: "/mirror",
				},
				ExpectedRequest: &http.ExpectedRequest{
					Request: http.Request{
						Path: "/mirror",
					},
				},
				Backend:    "infra-backend-v1",
				MirroredTo: "infra-backend-v2",
				Namespace:  ns,
			}, {
				Request: http.Request{
					Path: "/mirror-and-modify-headers",
					Headers: map[string]string{
						"X-Header-Remove":     "remove-val",
						"X-Header-Add-Append": "append-val-1",
					},
				},
				ExpectedRequest: &http.ExpectedRequest{
					Request: http.Request{
						Path: "/mirror-and-modify-headers",
						Headers: map[string]string{
							"X-Header-Add":        "header-val-1",
							"X-Header-Add-Append": "append-val-1,header-val-2",
							"X-Header-Set":        "set-overwrites-values",
						},
					},
					AbsentHeaders: []string{"X-Header-Remove"},
				},
				Namespace:  ns,
				Backend:    "infra-backend-v1",
				MirroredTo: "infra-backend-v2",
			},
		}
		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
				http.ExpectMirroredRequest(t, suite.Client, suite.Clientset, ns, tc.MirroredTo, tc.Request.Path)
			})
		}
	},
}
View Source
var HTTPRouteResponseHeaderModifier = suite.ConformanceTest{
	ShortName:   "HTTPRouteResponseHeaderModifier",
	Description: "An HTTPRoute has response header modifier filters applied correctly",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPResponseHeaderModification,
	},
	Manifests: []string{"tests/httproute-response-header-modifier.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "response-header-modifier", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{{
			Request: http.Request{
				Path: "/set",
			},
			BackendSetResponseHeaders: map[string]string{
				"Some-Other-Header": "val",
			},
			Response: http.Response{
				Headers: map[string]string{
					"Some-Other-Header": "val",
					"X-Header-Set":      "set-overwrites-values",
				},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/set",
			},
			BackendSetResponseHeaders: map[string]string{
				"Some-Other-Header": "val",
				"X-Header-Set":      "some-other-value",
			},
			Response: http.Response{
				Headers: map[string]string{
					"Some-Other-Header": "val",
					"X-Header-Set":      "set-overwrites-values",
				},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/add",
			},
			BackendSetResponseHeaders: map[string]string{
				"Some-Other-Header": "val",
			},
			Response: http.Response{
				Headers: map[string]string{
					"Some-Other-Header": "val",
					"X-Header-Add":      "add-appends-values",
				},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/add",
			},
			BackendSetResponseHeaders: map[string]string{
				"Some-Other-Header": "val",
				"X-Header-Add":      "some-other-value",
			},
			Response: http.Response{
				Headers: map[string]string{
					"Some-Other-Header": "val",
					"X-Header-Add":      "some-other-value,add-appends-values",
				},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/remove",
			},
			BackendSetResponseHeaders: map[string]string{
				"X-Header-Remove": "val",
			},
			Response: http.Response{
				AbsentHeaders: []string{"X-Header-Remove"},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/multiple",
			},
			BackendSetResponseHeaders: map[string]string{
				"X-Header-Set-2":    "set-val-2",
				"X-Header-Add-2":    "add-val-2",
				"X-Header-Remove-2": "remove-val-2",
				"Another-Header":    "another-header-val",
				"X-Header-Remove-1": "val",
			},
			Response: http.Response{
				Headers: map[string]string{
					"X-Header-Set-1": "header-set-1",
					"X-Header-Set-2": "header-set-2",
					"X-Header-Add-1": "header-add-1",
					"X-Header-Add-2": "add-val-2,header-add-2",
					"X-Header-Add-3": "header-add-3",
					"Another-Header": "another-header-val",
				},
				AbsentHeaders: []string{"X-Header-Remove-1", "X-Header-Remove-2"},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/case-insensitivity",
			},
			BackendSetResponseHeaders: map[string]string{
				"x-header-set":    "original-val-set",
				"x-header-add":    "original-val-add",
				"x-header-remove": "original-val-remove",
				"Another-Header":  "another-header-val",
			},
			Response: http.Response{
				Headers: map[string]string{
					"X-Header-Set":      "header-set",
					"X-Header-Add":      "original-val-add,header-add",
					"X-Lowercase-Add":   "lowercase-add",
					"X-Mixedcase-Add-1": "mixedcase-add-1",
					"X-Mixedcase-Add-2": "mixedcase-add-2",
					"X-Uppercase-Add":   "uppercase-add",
					"Another-Header":    "another-header-val",
				},
				AbsentHeaders: []string{"x-header-remove", "X-Header-Remove"},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}, {
			Request: http.Request{
				Path: "/response-and-request-header-modifiers",
				Headers: map[string]string{
					"X-Header-Remove":     "remove-val",
					"X-Header-Add-Append": "append-val-1",
					"X-Header-Echo":       "echo",
				},
			},
			BackendSetResponseHeaders: map[string]string{
				"X-Header-Set-2":    "set-val-2",
				"X-Header-Add-2":    "add-val-2",
				"X-Header-Remove-2": "remove-val-2",
				"Another-Header":    "another-header-val",
				"X-Header-Remove-1": "remove-val-1",
				"X-Header-Echo":     "echo",
			},
			ExpectedRequest: &http.ExpectedRequest{
				Request: http.Request{
					Path: "/response-and-request-header-modifiers",
					Headers: map[string]string{
						"X-Header-Add":        "header-val-1",
						"X-Header-Set":        "set-overwrites-values",
						"X-Header-Add-Append": "append-val-1,header-val-2",
						"X-Header-Echo":       "echo",
					},
				},
				AbsentHeaders: []string{"X-Header-Remove"},
			},
			Response: http.Response{
				Headers: map[string]string{
					"X-Header-Set-1": "header-set-1",
					"X-Header-Set-2": "header-set-2",
					"X-Header-Add-1": "header-add-1",
					"X-Header-Add-2": "add-val-2,header-add-2",
					"Another-Header": "another-header-val",
					"X-Header-Echo":  "echo",
				},
				AbsentHeaders: []string{"X-Header-Remove-1", "X-Header-Remove-2"},
			},
			Backend:   "infra-backend-v1",
			Namespace: ns,
		}}

		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteRewriteHost = suite.ConformanceTest{
	ShortName:   "HTTPRouteRewriteHost",
	Description: "An HTTPRoute with hostname rewrite filter",
	Manifests:   []string{"tests/httproute-rewrite-host.yaml"},
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPRouteHostRewrite,
	},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "rewrite-host", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{
			{
				Request: http.Request{
					Path: "/one",
					Host: "rewrite.example",
				},
				ExpectedRequest: &http.ExpectedRequest{
					Request: http.Request{
						Path: "/one",
						Host: "one.example.org",
					},
				},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			}, {
				Request: http.Request{
					Path: "/two",
					Host: "rewrite.example",
				},
				ExpectedRequest: &http.ExpectedRequest{
					Request: http.Request{
						Path: "/two",
						Host: "example.org",
					},
				},
				Backend:   "infra-backend-v2",
				Namespace: ns,
			}, {
				Request: http.Request{
					Path: "/rewrite-host-and-modify-headers",
					Host: "rewrite.example",
					Headers: map[string]string{
						"X-Header-Remove":     "remove-val",
						"X-Header-Add-Append": "append-val-1",
					},
				},
				ExpectedRequest: &http.ExpectedRequest{
					Request: http.Request{
						Path: "/rewrite-host-and-modify-headers",
						Host: "test.example.org",
						Headers: map[string]string{
							"X-Header-Add":        "header-val-1",
							"X-Header-Add-Append": "append-val-1,header-val-2",
							"X-Header-Set":        "set-overwrites-values",
						},
					},
					AbsentHeaders: []string{"X-Header-Remove"},
				},
				Backend:   "infra-backend-v2",
				Namespace: ns,
			},
		}
		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteRewritePath = suite.ConformanceTest{
	ShortName:   "HTTPRouteRewritePath",
	Description: "An HTTPRoute with path rewrite filter",
	Manifests:   []string{"tests/httproute-rewrite-path.yaml"},
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
		suite.SupportHTTPRoutePathRewrite,
	},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "rewrite-path", Namespace: ns}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		testCases := []http.ExpectedResponse{
			{
				Request: http.Request{
					Path: "/prefix/one/two",
				},
				ExpectedRequest: &http.ExpectedRequest{
					Request: http.Request{
						Path: "/one/two",
					},
				},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path: "/full/one/two",
				},
				ExpectedRequest: &http.ExpectedRequest{
					Request: http.Request{
						Path: "/one",
					},
				},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path: "/full/rewrite-path-and-modify-headers/test",
					Headers: map[string]string{
						"X-Header-Remove":     "remove-val",
						"X-Header-Add-Append": "append-val-1",
						"X-Header-Set":        "set-val",
					},
				},
				ExpectedRequest: &http.ExpectedRequest{
					Request: http.Request{
						Path: "/test",
						Headers: map[string]string{
							"X-Header-Add":        "header-val-1",
							"X-Header-Add-Append": "append-val-1,header-val-2",
							"X-Header-Set":        "set-overwrites-values",
						},
					},
					AbsentHeaders: []string{"X-Header-Remove"},
				},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
			{
				Request: http.Request{
					Path: "/prefix/rewrite-path-and-modify-headers/one",
					Headers: map[string]string{
						"X-Header-Remove":     "remove-val",
						"X-Header-Add-Append": "append-val-1",
						"X-Header-Set":        "set-val",
					},
				},
				ExpectedRequest: &http.ExpectedRequest{
					Request: http.Request{
						Path: "/prefix/one",
						Headers: map[string]string{
							"X-Header-Add":        "header-val-1",
							"X-Header-Add-Append": "append-val-1,header-val-2",
							"X-Header-Set":        "set-overwrites-values",
						},
					},
					AbsentHeaders: []string{"X-Header-Remove"},
				},
				Backend:   "infra-backend-v1",
				Namespace: ns,
			},
		}
		for i := range testCases {

			tc := testCases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				t.Parallel()
				http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
			})
		}
	},
}
View Source
var HTTPRouteSimpleSameNamespace = suite.ConformanceTest{
	ShortName:   "HTTPRouteSimpleSameNamespace",
	Description: "A single HTTPRoute in the gateway-conformance-infra namespace attaches to a Gateway in the same namespace",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportHTTPRoute,
	},
	Manifests: []string{"tests/httproute-simple-same-namespace.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := v1beta1.Namespace("gateway-conformance-infra")
		routeNN := types.NamespacedName{Name: "gateway-conformance-infra-test", Namespace: string(ns)}
		gwNN := types.NamespacedName{Name: "same-namespace", Namespace: string(ns)}
		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)

		t.Run("Simple HTTP request should reach infra-backend", func(t *testing.T) {
			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
				Request:   http.Request{Path: "/"},
				Response:  http.Response{StatusCode: 200},
				Backend:   "infra-backend-v1",
				Namespace: "gateway-conformance-infra",
			})
		})
	},
}
View Source
var MeshBasic = suite.ConformanceTest{
	ShortName:   "MeshBasic",
	Description: "A mesh client can communicate with a mesh server. This tests basic reachability with no configuration applied.",
	Features: []suite.SupportedFeature{
		suite.SupportMesh,
	},
	Manifests: []string{},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		client := echo.ConnectToApp(t, s, echo.MeshAppEchoV1)
		cases := []http.ExpectedResponse{{
			Request: http.Request{
				Host:   "echo",
				Method: "GET",
			},
			Response: http.Response{
				StatusCode: 200,
			},
		}}
		for i := range cases {

			tc := cases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				client.MakeRequestAndExpectEventuallyConsistentResponse(t, tc, s.TimeoutConfig)
			})
		}
	},
}
View Source
var MeshConsumerRoute = suite.ConformanceTest{
	ShortName:   "MeshConsumerRoute",
	Description: "An HTTPRoute in a namespace other than its parentRef's namespace only affects requests from the route's namespace",
	Features: []suite.SupportedFeature{
		suite.SupportMesh,
		suite.SupportHTTPRoute,
		suite.SupportHTTPResponseHeaderModification,
	},
	Manifests: []string{"tests/mesh-consumer-route.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		consumerClient := echo.ConnectToAppInNamespace(t, s, echo.MeshAppEchoV1, "gateway-conformance-mesh-consumer")
		consumerCases := []http.ExpectedResponse{
			{
				TestCaseName: "request from consumer route's namespace modified by HTTPRoute",
				Request: http.Request{
					Host:   "echo-v1.gateway-conformance-mesh",
					Method: "GET",
					Path:   "/",
				},
				Response: http.Response{
					StatusCode: 200,
					Headers: map[string]string{
						"X-Header-Set": "set",
					},
				},
				Backend: "echo-v1",
			},
		}
		producerClient := echo.ConnectToAppInNamespace(t, s, echo.MeshAppEchoV1, "gateway-conformance-mesh")
		producerCases := []http.ExpectedResponse{
			{
				TestCaseName: "request not from consumer route's namespace not modified by HTTPRoute",
				Request: http.Request{
					Host:   "echo-v1.gateway-conformance-mesh",
					Method: "GET",
					Path:   "/",
				},
				Response: http.Response{
					StatusCode:    200,
					AbsentHeaders: []string{"X-Header-Set"},
				},
				Backend: "echo-v1",
			},
		}
		for i, tc := range consumerCases {
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				consumerClient.MakeRequestAndExpectEventuallyConsistentResponse(t, tc, s.TimeoutConfig)
			})
		}
		for i, tc := range producerCases {
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				producerClient.MakeRequestAndExpectEventuallyConsistentResponse(t, tc, s.TimeoutConfig)
			})
		}
	},
}
View Source
var MeshFrontend = suite.ConformanceTest{
	ShortName:   "MeshFrontend",
	Description: "Mesh rules should only apply to the associated frontend",
	Features: []suite.SupportedFeature{
		suite.SupportMesh,
		suite.SupportHTTPRoute,
		suite.SupportHTTPResponseHeaderModification,
	},
	Manifests: []string{"tests/mesh-frontend.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		client := echo.ConnectToApp(t, s, echo.MeshAppEchoV1)
		v2 := echo.ConnectToApp(t, s, echo.MeshAppEchoV2)
		cases := []http.ExpectedResponse{
			{
				TestCaseName: "Send to service",
				Request: http.Request{
					Host:   "echo-v2",
					Method: "GET",
				},
				Response: http.Response{
					StatusCode: 200,

					Headers: map[string]string{
						"X-Header-Set": "set",
					},
				},
				Backend: "echo-v2",
			},
			{
				TestCaseName: "Send to pod IP",
				Request: http.Request{
					Host:   v2.Address,
					Method: "GET",
				},
				Response: http.Response{
					StatusCode:    200,
					AbsentHeaders: []string{"X-Header-Set"},
				},
				Backend: "echo-v2",
			},
		}
		for i := range cases {

			tc := cases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				client.MakeRequestAndExpectEventuallyConsistentResponse(t, tc, s.TimeoutConfig)
			})
		}
	},
}
View Source
var MeshFrontendHostname = suite.ConformanceTest{
	ShortName:   "MeshFrontendHostname",
	Description: "Mesh parentRef matches Service IP (not Host)",
	Features: []suite.SupportedFeature{
		suite.SupportMesh,
	},
	Manifests: []string{"tests/mesh-frontend.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		client := echo.ConnectToApp(t, s, echo.MeshAppEchoV1)
		cases := []http.ExpectedResponse{
			{
				TestCaseName: "Send to service with wrong hostname",
				Request: http.Request{
					Host: "echo-v2",
					Headers: map[string]string{
						"Host": "echo-v1",
					},
					Method: "GET",
				},
				Response: http.Response{
					StatusCode: 200,

					Headers: map[string]string{
						"X-Header-Set": "set",
					},
				},
				Backend: "echo-v2",
			},
			{
				TestCaseName: "Send to other service with matching hostname",
				Request: http.Request{
					Host: "echo-v1",
					Headers: map[string]string{
						"Host": "echo-v2",
					},
					Method: "GET",
				},
				Response: http.Response{
					StatusCode:    200,
					AbsentHeaders: []string{"X-Header-Set"},
				},
				Backend: "echo-v1",
			},
		}
		for i := range cases {

			tc := cases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				client.MakeRequestAndExpectEventuallyConsistentResponse(t, tc, s.TimeoutConfig)
			})
		}
	},
}
View Source
var MeshPorts = suite.ConformanceTest{
	ShortName:   "MeshPorts",
	Description: "A mesh route can optionally configure 'port' in parentRef",
	Features: []suite.SupportedFeature{
		suite.SupportMesh,
		suite.SupportHTTPRoute,
		suite.SupportHTTPResponseHeaderModification,
	},
	Manifests: []string{"tests/mesh-ports.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		client := echo.ConnectToApp(t, s, echo.MeshAppEchoV1)
		cases := []http.ExpectedResponse{
			{
				TestCaseName: "Explicit port set, send to that port",
				Request: http.Request{
					Host:   "echo-v1",
					Method: "GET",
				},
				Response: http.Response{
					StatusCode: 200,

					Headers: map[string]string{
						"X-Header-Set": "v1",
					},
				},
				Backend: "echo-v1",
			},
			{
				TestCaseName: "Explicit port, send to an excluded port",
				Request: http.Request{
					Host:   "echo-v1:8080",
					Method: "GET",
				},
				Response: http.Response{
					StatusCode: 200,

					AbsentHeaders: []string{"X-Header-Set"},
				},
				Backend: "echo-v1",
			},
			{
				TestCaseName: "No port set",
				Request: http.Request{
					Host:   "echo-v2",
					Method: "GET",
				},
				Response: http.Response{
					StatusCode: 200,
					Headers: map[string]string{
						"X-Header-Set": "v2",
					},
				},
				Backend: "echo-v2",
			},
			{
				TestCaseName: "No port set",
				Request: http.Request{
					Host:   "echo-v2:8080",
					Method: "GET",
				},
				Response: http.Response{
					StatusCode: 200,
					Headers: map[string]string{
						"X-Header-Set": "v2",
					},
				},
				Backend: "echo-v2",
			},
		}
		for i := range cases {

			tc := cases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				client.MakeRequestAndExpectEventuallyConsistentResponse(t, tc, s.TimeoutConfig)
			})
		}
	},
}
View Source
var MeshTrafficSplit = suite.ConformanceTest{
	ShortName:   "MeshTrafficSplit",
	Description: "A mesh client can send traffic to a Service which is split between two versions",
	Features: []suite.SupportedFeature{
		suite.SupportMesh,
	},
	Manifests: []string{"tests/mesh-split.yaml"},
	Test: func(t *testing.T, s *suite.ConformanceTestSuite) {
		client := echo.ConnectToApp(t, s, echo.MeshAppEchoV1)
		cases := []http.ExpectedResponse{
			{
				Request: http.Request{
					Host:   "echo",
					Method: "GET",
					Path:   "/v1",
				},
				Response: http.Response{
					StatusCode: 200,
				},
				Backend: "echo-v1",
			},
			{
				Request: http.Request{
					Host:   "echo",
					Method: "GET",
					Path:   "/v2",
				},
				Response: http.Response{
					StatusCode: 200,
				},
				Backend: "echo-v2",
			},
		}
		for i := range cases {

			tc := cases[i]
			t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
				client.MakeRequestAndExpectEventuallyConsistentResponse(t, tc, s.TimeoutConfig)
			})
		}
	},
}
View Source
var TLSRouteInvalidReferenceGrant = suite.ConformanceTest{
	ShortName:   "TLSRouteInvalidReferenceGrant",
	Description: "A single TLSRoute in the gateway-conformance-infra namespace, with a backendRef in another namespace without valid ReferenceGrant, should have the ResolvedRefs condition set to False",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportTLSRoute,
		suite.SupportReferenceGrant,
	},
	Manifests: []string{"tests/tlsroute-invalid-reference-grant.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		routeNN := types.NamespacedName{Name: "gateway-conformance-infra-test", Namespace: "gateway-conformance-infra"}
		gwNN := types.NamespacedName{Name: "gateway-tlsroute-referencegrant", Namespace: "gateway-conformance-infra"}

		kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

		t.Run("TLSRoute with BackendRef in another namespace and no ReferenceGrant covering the Service has a ResolvedRefs Condition with status False and Reason RefNotPermitted", func(t *testing.T) {
			resolvedRefsCond := metav1.Condition{
				Type:   string(v1beta1.RouteConditionResolvedRefs),
				Status: metav1.ConditionFalse,
				Reason: string(v1beta1.RouteReasonRefNotPermitted),
			}

			kubernetes.TLSRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
		})
	},
}
View Source
var TLSRouteSimpleSameNamespace = suite.ConformanceTest{
	ShortName:   "TLSRouteSimpleSameNamespace",
	Description: "A single TLSRoute in the gateway-conformance-infra namespace attaches to a Gateway in the same namespace",
	Features: []suite.SupportedFeature{
		suite.SupportGateway,
		suite.SupportTLSRoute,
	},
	Manifests: []string{"tests/tlsroute-simple-same-namespace.yaml"},
	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
		ns := "gateway-conformance-infra"
		routeNN := types.NamespacedName{Name: "gateway-conformance-infra-test", Namespace: ns}
		gwNN := types.NamespacedName{Name: "gateway-tlsroute", Namespace: ns}
		certNN := types.NamespacedName{Name: "tls-passthrough-checks-certificate", Namespace: ns}

		kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{ns})

		gwAddr, hostnames := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
		if len(hostnames) != 1 {
			t.Fatalf("unexpected error in test configuration, found %d hostnames", len(hostnames))
		}
		serverStr := string(hostnames[0])

		cPem, keyPem, err := GetTLSSecret(suite.Client, certNN)
		if err != nil {
			t.Fatalf("unexpected error finding TLS secret: %v", err)
		}
		t.Run("Simple TLS request matching TLSRoute should reach infra-backend", func(t *testing.T) {
			tls.MakeTLSRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, cPem, keyPem, serverStr,
				http.ExpectedResponse{
					Request:   http.Request{Host: serverStr, Path: "/"},
					Backend:   "tls-backend",
					Namespace: "gateway-conformance-infra",
				})
		})
	},
}

Functions

func GetTLSSecret added in v0.6.1

func GetTLSSecret(client client.Client, secretName types.NamespacedName) ([]byte, []byte, error)

GetTLSSecret fetches the named Secret and converts both cert and key to []byte

Types

This section is empty.

Source Files

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL