diff --git a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/plugin_test.go b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/plugin_test.go index f3c3509b0bfb..304a53b75129 100644 --- a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/plugin_test.go +++ b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/plugin_test.go @@ -378,6 +378,92 @@ var _ = Describe("MeshHTTPRoute", func() { proxy: proxy, } }()), + Entry("gateway-default-meshexternalservice", func() outboundsTestCase { + meshExtSvc := meshexternalservice_api.MeshExternalServiceResource{ + Meta: &test_model.ResourceMeta{Name: "example", Mesh: "default"}, + Spec: &meshexternalservice_api.MeshExternalService{ + Match: meshexternalservice_api.Match{ + Type: pointer.To(meshexternalservice_api.HostnameGeneratorType), + Port: 9090, + Protocol: core_mesh.ProtocolHTTP, + }, + Endpoints: []meshexternalservice_api.Endpoint{ + { + Address: "example.com", + Port: pointer.To(meshexternalservice_api.Port(10000)), + }, + }, + }, + Status: &meshexternalservice_api.MeshExternalServiceStatus{ + VIP: meshexternalservice_api.VIP{ + IP: "10.20.20.1", + }, + }, + } + gateway := &core_mesh.MeshGatewayResource{ + Meta: &test_model.ResourceMeta{Name: "sample-gateway", Mesh: "default"}, + Spec: &mesh_proto.MeshGateway{ + Selectors: []*mesh_proto.Selector{ + { + Match: map[string]string{ + mesh_proto.ServiceTag: "sample-gateway", + }, + }, + }, + Conf: &mesh_proto.MeshGateway_Conf{ + Listeners: []*mesh_proto.MeshGateway_Listener{ + { + Protocol: mesh_proto.MeshGateway_Listener_HTTP, + Port: 8080, + }, + }, + }, + }, + } + meshHttpRoute := api.MeshHTTPRouteResource{ + Meta: &test_model.ResourceMeta{Name: "http-route", Mesh: "default"}, + Spec: &api.MeshHTTPRoute{ + TargetRef: builders.TargetRefMeshGateway("sample-gateway"), + To: []api.To{ + { + TargetRef: common_api.TargetRef{ + Kind: common_api.MeshExternalService, + Name: "example", + }, + Rules: []api.Rule{ + { + Matches: []api.Match{{ + Path: &api.PathMatch{ + Type: api.PathPrefix, + Value: "/", + }, + }}, + Default: api.RuleConf{ + BackendRefs: &[]common_api.BackendRef{{ + TargetRef: common_api.TargetRef{ + Kind: common_api.MeshExternalService, + Name: "external", + }, + Port: pointer.To(uint32(9090)), + Weight: pointer.To(uint(100)), + }}, + }, + }, + }, + }, + }, + }, + } + + dp, proxy := dppForMeshExternalService(&meshExtSvc) + egress := builders.ZoneEgress().WithPort(10002).Build() + mc := meshContextWithResources(dp.Build(), &meshExtSvc, egress, gateway, &meshHttpRoute) + + return outboundsTestCase{ + xdsContext: *xds_builders.Context().WithMeshContext(mc).Build(), + proxy: proxy, + } + }()), Entry("httproute-meshexternalservice", func() outboundsTestCase { meshExtSvc := meshexternalservice_api.MeshExternalServiceResource{ Meta: &test_model.ResourceMeta{Name: "example", Mesh: "default"}, diff --git a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.clusters.golden.yaml b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.clusters.golden.yaml new file mode 100644 index 000000000000..349f9dc73528 --- /dev/null +++ b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.clusters.golden.yaml @@ -0,0 +1,40 @@ +resources: +- name: default_example___extsvc_9090 + resource: + '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster + edsClusterConfig: + edsConfig: + ads: {} + initialFetchTimeout: 0s + resourceApiVersion: V3 + name: default_example___extsvc_9090 + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + alpnProtocols: + - kuma + combinedValidationContext: + defaultValidationContext: + matchTypedSubjectAltNames: + - matcher: + exact: spiffe://default/zone-egress + sanType: URI + validationContextSdsSecretConfig: + name: mesh_ca:secret:default + sdsConfig: + ads: {} + resourceApiVersion: V3 + tlsCertificateSdsSecretConfigs: + - name: identity_cert:secret:default + sdsConfig: + ads: {} + resourceApiVersion: V3 + sni: afce62f771ed74b78.example.9090.default.mes + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + httpProtocolOptions: {} diff --git a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.endpoints.golden.yaml b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.endpoints.golden.yaml new file mode 100644 index 000000000000..9eff51c378e0 --- /dev/null +++ b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.endpoints.golden.yaml @@ -0,0 +1,13 @@ +resources: +- name: default_example___extsvc_9090 + resource: + '@type': type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment + clusterName: default_example___extsvc_9090 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 127.0.0.1 + portValue: 10002 + loadBalancingWeight: 1 diff --git a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.listeners.golden.yaml b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.listeners.golden.yaml new file mode 100644 index 000000000000..54648b4b556a --- /dev/null +++ b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.listeners.golden.yaml @@ -0,0 +1,43 @@ +resources: +- name: outbound:10.20.20.1:9090 + resource: + '@type': type.googleapis.com/envoy.config.listener.v3.Listener + address: + socketAddress: + address: 10.20.20.1 + portValue: 9090 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + normalizePath: true + routeConfig: + name: default_example___extsvc_9090 + requestHeadersToAdd: + - header: + key: x-kuma-tags + value: '&kuma.io/protocol=http&&kuma.io/service=web&' + validateClusters: false + virtualHosts: + - domains: + - '*' + name: default_example___extsvc_9090 + routes: + - match: + prefix: / + name: 9Zuf5Tg79OuZcQITwBbQykxAk2u4fRKrwYn3//AL4Yo= + route: + autoHostRewrite: true + cluster: default_example___extsvc_9090 + timeout: 0s + statPrefix: default_example___extsvc_9090 + metadata: + filterMetadata: + io.kuma.tags: {} + name: outbound:10.20.20.1:9090 + trafficDirection: OUTBOUND diff --git a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.clusters.golden.yaml b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.routes.golden.yaml similarity index 100% rename from pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.clusters.golden.yaml rename to pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.routes.golden.yaml diff --git a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.endpoints.golden.yaml b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.secrets.golden.yaml similarity index 100% rename from pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.endpoints.golden.yaml rename to pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-default-meshexternalservice.secrets.golden.yaml diff --git a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.listeners.golden.yaml b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.listeners.golden.yaml deleted file mode 100644 index d9e3529e6bb7..000000000000 --- a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.listeners.golden.yaml +++ /dev/null @@ -1,60 +0,0 @@ -resources: -- name: sample-gateway:HTTP:8080 - resource: - '@type': type.googleapis.com/envoy.config.listener.v3.Listener - address: - socketAddress: - address: 192.168.0.1 - portValue: 8080 - enableReusePort: true - filterChains: - - filters: - - name: envoy.filters.network.http_connection_manager - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - commonHttpProtocolOptions: - headersWithUnderscoresAction: REJECT_REQUEST - idleTimeout: 300s - http2ProtocolOptions: - allowConnect: true - initialConnectionWindowSize: 1048576 - initialStreamWindowSize: 65536 - maxConcurrentStreams: 100 - httpFilters: - - name: envoy.filters.http.local_ratelimit - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit - statPrefix: rate_limit - - name: gzip-compress - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor - compressorLibrary: - name: gzip - typedConfig: - '@type': type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip - responseDirectionConfig: - disableOnEtagHeader: true - - name: envoy.filters.http.router - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - mergeSlashes: true - normalizePath: true - pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT - rds: - configSource: - ads: {} - initialFetchTimeout: 0s - resourceApiVersion: V3 - routeConfigName: sample-gateway:HTTP:8080:* - requestHeadersTimeout: 0.500s - serverName: Kuma Gateway - statPrefix: sample-gateway - streamIdleTimeout: 5s - useRemoteAddress: true - listenerFilters: - - name: envoy.filters.listener.tls_inspector - typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector - name: sample-gateway:HTTP:8080 - perConnectionBufferLimitBytes: 32768 - trafficDirection: INBOUND diff --git a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.routes.golden.yaml b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.routes.golden.yaml deleted file mode 100644 index 34802521f55a..000000000000 --- a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.routes.golden.yaml +++ /dev/null @@ -1,20 +0,0 @@ -resources: -- name: sample-gateway:HTTP:8080:* - resource: - '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration - ignorePortInHostMatching: true - name: sample-gateway:HTTP:8080:* - requestHeadersToRemove: - - x-kuma-tags - validateClusters: false - virtualHosts: - - domains: - - '*' - name: '*' - routes: - - match: - path: /external - name: z3UA6bBdochDk439G0ZMA+OTdJbkinz5R7Zo/LQ5ASA= - - match: - prefix: /external/ - name: z3UA6bBdochDk439G0ZMA+OTdJbkinz5R7Zo/LQ5ASA= diff --git a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.secrets.golden.yaml b/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.secrets.golden.yaml deleted file mode 100644 index 0967ef424bce..000000000000 --- a/pkg/plugins/policies/meshhttproute/plugin/v1alpha1/testdata/gateway-meshexternalservice.secrets.golden.yaml +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/pkg/plugins/runtime/gateway/cluster_generator.go b/pkg/plugins/runtime/gateway/cluster_generator.go index 97a38e1c0cf3..ad7eaa21dcf2 100644 --- a/pkg/plugins/runtime/gateway/cluster_generator.go +++ b/pkg/plugins/runtime/gateway/cluster_generator.go @@ -58,7 +58,8 @@ func (c *ClusterGenerator) GenerateClusters(ctx context.Context, xdsCtx xds_cont log.Info("skipping backendRef", "backendRef", dest.BackendRef.LegacyBackendRef) continue } - isExternalCluster = dest.BackendRef.LegacyBackendRef.Kind == "MeshExternalService" + isExternalService := xdsCtx.Mesh.IsExternalService(service) + isExternalCluster = isExternalService && !xdsCtx.Mesh.Resource.ZoneEgressEnabled() } else { service = dest.Destination[mesh_proto.ServiceTag] diff --git a/pkg/test/resources/builders/targetref_builder.go b/pkg/test/resources/builders/targetref_builder.go index bd1fb9881695..b38ddd91a0d3 100644 --- a/pkg/test/resources/builders/targetref_builder.go +++ b/pkg/test/resources/builders/targetref_builder.go @@ -55,3 +55,10 @@ func TargetRefMeshExternalService(name string) common_api.TargetRef { Name: name, } } + +func TargetRefMeshGateway(name string) *common_api.TargetRef { + return &common_api.TargetRef{ + Kind: common_api.MeshGateway, + Name: name, + } +} diff --git a/pkg/xds/envoy/tags/match.go b/pkg/xds/envoy/tags/match.go index b1f859b5beb3..393095bea9bb 100644 --- a/pkg/xds/envoy/tags/match.go +++ b/pkg/xds/envoy/tags/match.go @@ -116,6 +116,8 @@ func FromTargetRef(targetRef common_api.TargetRef) (Tags, bool) { switch targetRef.Kind { case common_api.MeshService: service = targetRef.Name + case common_api.MeshExternalService: + service = targetRef.Name case common_api.MeshServiceSubset: service = targetRef.Name tags = targetRef.Tags diff --git a/test/e2e_env/kubernetes/gateway/gateway.go b/test/e2e_env/kubernetes/gateway/gateway.go index 9b9f8b885840..9dae375228ab 100644 --- a/test/e2e_env/kubernetes/gateway/gateway.go +++ b/test/e2e_env/kubernetes/gateway/gateway.go @@ -236,7 +236,11 @@ spec: } } - httpRoute := func(name, path, destination string) []string { + httpRoute := func(name, path, destination, destinationKind, port string) []string { + portValue := "" + if port != "" { + portValue = "port: " + port + } return []string{ fmt.Sprintf(` apiVersion: kuma.io/v1alpha1 @@ -260,8 +264,9 @@ spec: value: "%s" default: backendRefs: - - kind: MeshService + - kind: %s name: "%s" + %s - targetRef: kind: Mesh hostnames: @@ -279,9 +284,10 @@ spec: - name: x-specific-hostname-header value: "true" backendRefs: - - kind: MeshService + - kind: %s name: "%s" -`, name, Config.KumaNamespace, path, destination, path, destination), + %s +`, name, Config.KumaNamespace, path, destinationKind, destination, portValue, path, destinationKind, destination, portValue), fmt.Sprintf(` apiVersion: kuma.io/v1alpha1 kind: MeshHTTPRoute @@ -312,9 +318,10 @@ spec: - name: x-listener-by-hostname-header value: "true" backendRefs: - - kind: MeshService + - kind: %s name: "%s" -`, name+"-hostname-specific", Config.KumaNamespace, path, destination), + %s +`, name+"-hostname-specific", Config.KumaNamespace, path, destinationKind, destination, portValue), } } @@ -465,7 +472,7 @@ spec: } basicRouting("MeshGatewayRoute", meshGatewayRoutes("internal-service", "/", "echo-server_simple-gateway_svc_80")) - basicRouting("MeshHTTPRoute", httpRoute("internal-service", "/", "echo-server_simple-gateway_svc_80")) + basicRouting("MeshHTTPRoute", httpRoute("internal-service", "/", "echo-server_simple-gateway_svc_80", "MeshService", "")) Context("Rate Limit", func() { rt := `apiVersion: kuma.io/v1alpha1 @@ -550,7 +557,7 @@ spec: header: name: x-header `, Config.KumaNamespace, meshName) - routes := httpRoute("test-server-mlbs", "/mlbs", "test-server-mlbs_simple-gateway_svc_80") + routes := httpRoute("test-server-mlbs", "/mlbs", "test-server-mlbs_simple-gateway_svc_80", "MeshService", "") BeforeAll(func() { err := NewClusterSetup(). @@ -1156,4 +1163,62 @@ spec: Expect(NewClusterSetup().Install(DeleteYamlK8s(route)).Setup(kubernetes.Cluster)).To(Succeed()) }) }) + + Context("MeshExternalService", func() { + meshExternalService := fmt.Sprintf(` +apiVersion: kuma.io/v1alpha1 +kind: MeshExternalService +metadata: + name: simple-gateway-mesh-external-service + namespace: %s + labels: + kuma.io/mesh: %s +spec: + match: + type: HostnameGenerator + port: 80 + protocol: http + endpoints: + - address: mes-echo-server.client-simple-gateway.svc.cluster.local + port: 80`, Config.KumaNamespace, meshName) + + routes := httpRoute("test-server-mes", "/mes", "simple-gateway-mesh-external-service", "MeshExternalService", "80") + + BeforeAll(func() { + err := NewClusterSetup(). + Install(MTLSMeshKubernetesWithEgressRouting(meshName)). + Install(testserver.Install( + testserver.WithNamespace(clientNamespace), + testserver.WithName("mes-echo-server"), + )). + Install(YamlK8s(meshExternalService)). + Install(YamlK8s(routes...)). + Setup(kubernetes.Cluster) + Expect(err).ToNot(HaveOccurred()) + }) + + E2EAfterAll(func() { + err := NewClusterSetup(). + Install(MTLSMeshKubernetes(meshName)). + Install(DeleteYamlK8s(routes...)). + Install(DeleteYamlK8s(meshExternalService)). + Setup(kubernetes.Cluster) + Expect(err).ToNot(HaveOccurred()) + }) + + It("should route to MeshExternalService", func() { + Eventually(func(g Gomega) { + responses, err := client.CollectResponsesByInstance( + kubernetes.Cluster, "demo-client", + "http://simple-gateway.simple-gateway:8080/mes", + client.WithHeader("host", "example.kuma.io"), + client.FromKubernetesPod(clientNamespace, "demo-client"), + client.WithHeader("x-header", "value"), + ) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(responses).To(HaveLen(1)) + }, "30s", "1s").MustPassRepeatedly(5).Should(Succeed()) + }) + }) } diff --git a/test/framework/setup.go b/test/framework/setup.go index b697fc993ff5..57075c0753ef 100644 --- a/test/framework/setup.go +++ b/test/framework/setup.go @@ -144,6 +144,24 @@ spec: return YamlK8s(mesh) } +func MTLSMeshKubernetesWithEgressRouting(name string) InstallFunc { + mesh := fmt.Sprintf(` +apiVersion: kuma.io/v1alpha1 +kind: Mesh +metadata: + name: %s +spec: + routing: + zoneEgress: true + mtls: + enabledBackend: ca-1 + backends: + - name: ca-1 + type: builtin +`, name) + return YamlK8s(mesh) +} + func MTLSMeshWithMeshServicesKubernetes(name string, meshServicesEnabled string) InstallFunc { mesh := fmt.Sprintf(` apiVersion: kuma.io/v1alpha1