diff --git a/api/openapi/specs/api.yaml b/api/openapi/specs/api.yaml index 76551cf89aa5..97c1c8bc4be7 100644 --- a/api/openapi/specs/api.yaml +++ b/api/openapi/specs/api.yaml @@ -172,6 +172,41 @@ paths: $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' + /meshes/{mesh}/{serviceType}/{serviceName}/_resources/hostnames: + get: + operationId: inspect-hostnames + summary: Returns hostnames for service + description: Returns hostnames for a service + tags: ["Inspect"] + parameters: + - in: path + name: mesh + example: default + schema: + type: string + required: true + description: The mesh the service is part of + - in: path + name: serviceType + example: meshservices + schema: + type: string + required: true + description: The type of the service (meshservices, meshmultizoneservices, meshexternalservices) + - in: path + name: serviceName + example: redis + schema: + type: string + required: true + description: The name of the service + responses: + '200': + $ref: '#/components/responses/InspectHostnamesResponse' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalServerError' components: schemas: Index: @@ -433,6 +468,45 @@ components: additionalProperties: $ref: '#/components/schemas/ResourceStats' description: A map of resource names to their corresponding statistics + InspectHostnames: + type: object + title: InspectHostnames + description: A list of hostnames + required: [total, items] + properties: + total: + type: integer + example: 200 + items: + type: array + items: + $ref: '#/components/schemas/InspectHostname' + InspectHostname: + type: object + title: InspectHostname + description: A list of zones in which the hostname is available in + required: [hostname, zones] + properties: + hostname: + type: string + description: Generated hostname + example: redis.redis-system.svc.east.mesh.local + zones: + type: array + items: + $ref: '#/components/schemas/InspectHostnameZone' + InspectHostnameZone: + type: object + title: InspectHostnameZone + description: A name of the zone with hostname generator that was used to generate the hostname + required: [name, hostnameGeneratorCoreName] + properties: + name: + type: string + example: east + hostnameGeneratorCoreName: + type: string + example: synced-kube-mesh-service.kuma-system responses: IndexResponse: description: A response for the index endpoint @@ -476,6 +550,15 @@ components: application/json: schema: $ref: '#/components/schemas/InspectRules' + InspectHostnamesResponse: + description: A response containing hostnames that match a service. + content: + application/json: + schema: + $ref: '#/components/schemas/InspectHostnames' + examples: + ResponseForDataplane: + $ref: '#/components/examples/InspectDataplanesForPolicyExample' BadRequest: description: Bad Request content: @@ -536,3 +619,13 @@ components: - type: Dataplane mesh: default name: dp-2 + InspectHostnamesExample: + value: + total: 100 + items: + - hostname: redis.redis-system.svc.east.mesh.local + zones: + - name: east + hostnameGeneratorCoreName: synced-kube-mesh-service.kuma-system + - name: west + hostnameGeneratorCoreName: synced-kube-mesh-service.kuma-system diff --git a/api/openapi/types/zz_generated.api.go b/api/openapi/types/zz_generated.api.go index 65cc1ae88128..14eaa252cbf0 100644 --- a/api/openapi/types/zz_generated.api.go +++ b/api/openapi/types/zz_generated.api.go @@ -109,6 +109,25 @@ type InspectDataplanesForPolicy struct { Total int `json:"total"` } +// InspectHostname A list of zones in which the hostname is available in +type InspectHostname struct { + // Hostname Generated hostname + Hostname string `json:"hostname"` + Zones []InspectHostnameZone `json:"zones"` +} + +// InspectHostnameZone A name of the zone with hostname generator that was used to generate the hostname +type InspectHostnameZone struct { + HostnameGeneratorCoreName string `json:"hostnameGeneratorCoreName"` + Name string `json:"name"` +} + +// InspectHostnames A list of hostnames +type InspectHostnames struct { + Items []InspectHostname `json:"items"` + Total int `json:"total"` +} + // InspectRules A list of rules for a dataplane type InspectRules struct { HttpMatches []externalRef0.HttpMatch `json:"httpMatches"` @@ -183,6 +202,9 @@ type InspectDataplanesConfigResponse = InspectDataplanesConfig // InspectDataplanesForPolicyResponse A list of proxies type InspectDataplanesForPolicyResponse = InspectDataplanesForPolicy +// InspectHostnamesResponse A list of hostnames +type InspectHostnamesResponse = InspectHostnames + // InspectRulesResponse A list of rules for a dataplane type InspectRulesResponse = InspectRules diff --git a/docs/generated/openapi.yaml b/docs/generated/openapi.yaml index 8fdbcf1a0f76..8183e6cb68d1 100644 --- a/docs/generated/openapi.yaml +++ b/docs/generated/openapi.yaml @@ -190,6 +190,44 @@ paths: $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' + /meshes/{mesh}/{serviceType}/{serviceName}/_resources/hostnames: + get: + operationId: inspect-hostnames + summary: Returns hostnames for service + description: Returns hostnames for a service + tags: + - Inspect + parameters: + - in: path + name: mesh + example: default + schema: + type: string + required: true + description: The mesh the service is part of + - in: path + name: serviceType + example: meshservices + schema: + type: string + required: true + description: >- + The type of the service (meshservices, meshmultizoneservices, + meshexternalservices) + - in: path + name: serviceName + example: redis + schema: + type: string + required: true + description: The name of the service + responses: + '200': + $ref: '#/components/responses/InspectHostnamesResponse' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalServerError' /meshes/{mesh}/meshaccesslogs/{name}: get: summary: Returns MeshAccessLog entity @@ -2330,6 +2368,53 @@ components: additionalProperties: $ref: '#/components/schemas/ResourceStats' description: A map of resource names to their corresponding statistics + InspectHostnames: + type: object + title: InspectHostnames + description: A list of hostnames + required: + - total + - items + properties: + total: + type: integer + example: 200 + items: + type: array + items: + $ref: '#/components/schemas/InspectHostname' + InspectHostname: + type: object + title: InspectHostname + description: A list of zones in which the hostname is available in + required: + - hostname + - zones + properties: + hostname: + type: string + description: Generated hostname + example: redis.redis-system.svc.east.mesh.local + zones: + type: array + items: + $ref: '#/components/schemas/InspectHostnameZone' + InspectHostnameZone: + type: object + title: InspectHostnameZone + description: >- + A name of the zone with hostname generator that was used to generate the + hostname + required: + - name + - hostnameGeneratorCoreName + properties: + name: + type: string + example: east + hostnameGeneratorCoreName: + type: string + example: synced-kube-mesh-service.kuma-system PolicyDescription: type: object required: @@ -11966,6 +12051,15 @@ components: application/json: schema: $ref: '#/components/schemas/InspectRules' + InspectHostnamesResponse: + description: A response containing hostnames that match a service. + content: + application/json: + schema: + $ref: '#/components/schemas/InspectHostnames' + examples: + ResponseForDataplane: + $ref: '#/components/examples/InspectDataplanesForPolicyExample' BadRequest: description: Bad Request content: @@ -12487,4 +12581,14 @@ components: - type: Dataplane mesh: default name: dp-2 + InspectHostnamesExample: + value: + total: 100 + items: + - hostname: redis.redis-system.svc.east.mesh.local + zones: + - name: east + hostnameGeneratorCoreName: synced-kube-mesh-service.kuma-system + - name: west + hostnameGeneratorCoreName: synced-kube-mesh-service.kuma-system diff --git a/pkg/api-server/endpoints_table_test.go b/pkg/api-server/endpoints_table_test.go index 0941f239a099..6ed1c9f8f89a 100644 --- a/pkg/api-server/endpoints_table_test.go +++ b/pkg/api-server/endpoints_table_test.go @@ -33,6 +33,10 @@ var _ = Describe("Endpoints", func() { apiTest(inputFile, apiServer, resourceStore) }, test.EntriesForFolder("resources/inspect/policies/_resources/dataplanes")) + DescribeTable("inspect for policies /meshes/{mesh}/{serviceType}/{policyName}/_resources/hostnames", func(inputFile string) { + apiTest(inputFile, apiServer, resourceStore) + }, test.EntriesForFolder("resources/inspect/services/_resources/hostnames")) + DescribeTable("inspect dataplane rules /meshes/{mesh}/dataplanes/{dpName}/_rules", func(inputFile string) { apiTest(inputFile, apiServer, resourceStore) }, test.EntriesForFolder("resources/inspect/dataplanes/_rules")) diff --git a/pkg/api-server/inspect_mesh_service.go b/pkg/api-server/inspect_mesh_service.go index 254b4b173b06..88d9430840c2 100644 --- a/pkg/api-server/inspect_mesh_service.go +++ b/pkg/api-server/inspect_mesh_service.go @@ -1,15 +1,32 @@ package api_server import ( + "fmt" + "sort" + "strings" + "github.com/emicklei/go-restful/v3" + "golang.org/x/exp/maps" + mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1" + "github.com/kumahq/kuma/api/openapi/types" "github.com/kumahq/kuma/pkg/core/resources/access" + hostnamegenerator_api "github.com/kumahq/kuma/pkg/core/resources/apis/hostnamegenerator/api/v1alpha1" + "github.com/kumahq/kuma/pkg/core/resources/apis/hostnamegenerator/hostname" core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh" + meshexternalservice_api "github.com/kumahq/kuma/pkg/core/resources/apis/meshexternalservice/api/v1alpha1" + mes_hostname "github.com/kumahq/kuma/pkg/core/resources/apis/meshexternalservice/hostname" + meshmultizoneservice_api "github.com/kumahq/kuma/pkg/core/resources/apis/meshmultizoneservice/api/v1alpha1" + mzms_hostname "github.com/kumahq/kuma/pkg/core/resources/apis/meshmultizoneservice/hostname" "github.com/kumahq/kuma/pkg/core/resources/apis/meshservice" meshservice_api "github.com/kumahq/kuma/pkg/core/resources/apis/meshservice/api/v1alpha1" + meshservice_hostname "github.com/kumahq/kuma/pkg/core/resources/apis/meshservice/hostname" "github.com/kumahq/kuma/pkg/core/resources/manager" "github.com/kumahq/kuma/pkg/core/resources/model" "github.com/kumahq/kuma/pkg/core/resources/store" + rest_errors "github.com/kumahq/kuma/pkg/core/rest/errors" + "github.com/kumahq/kuma/pkg/core/validators" + util_maps "github.com/kumahq/kuma/pkg/util/maps" ) func addInspectMeshServiceEndpoints( @@ -24,6 +41,13 @@ func addInspectMeshServiceEndpoints( Param(ws.PathParameter("name", "mesh service name").DataType("string")). Param(ws.PathParameter("mesh", "mesh name").DataType("string")), ) + ws.Route( + ws.GET("/meshes/{mesh}/{serviceType}/{name}/_resources/hostnames"). + To(matchingHostnames(rm)). + Doc("inspect service hostnames"). + Param(ws.PathParameter("name", "mesh service name").DataType("string")). + Param(ws.PathParameter("mesh", "mesh name").DataType("string")), + ) } func matchingDataplanesForMeshServices(resManager manager.ResourceManager, resourceAccess access.ResourceAccess) restful.RouteFunction { @@ -43,3 +67,109 @@ func matchingDataplanesForMeshServices(resManager manager.ResourceManager, resou ) } } + +func matchingHostnames(resManager manager.ResourceManager) restful.RouteFunction { + generatorsForType := map[string]hostname.HostnameGenerator{ + "meshservices": meshservice_hostname.NewMeshServiceHostnameGenerator(resManager), + "meshexternalservices": mes_hostname.NewMeshExternalServiceHostnameGenerator(resManager), + "meshmultizoneservices": mzms_hostname.NewMeshMultiZoneServiceHostnameGenerator(resManager), + } + typeDescForType := map[string]model.ResourceTypeDescriptor{ + "meshservices": meshservice_api.MeshServiceResourceTypeDescriptor, + "meshexternalservices": meshexternalservice_api.MeshExternalServiceResourceTypeDescriptor, + "meshmultizoneservices": meshmultizoneservice_api.MeshMultiZoneServiceResourceTypeDescriptor, + } + + return func(request *restful.Request, response *restful.Response) { + svcName := request.PathParameter("name") + svcMesh := request.PathParameter("mesh") + svcType := request.PathParameter("serviceType") + + desc, ok := typeDescForType[svcType] + if !ok { + rest_errors.HandleError( + request.Request.Context(), + response, + &validators.ValidationError{}, + fmt.Sprintf("only %s are available for inspection", strings.Join(util_maps.SortedKeys(typeDescForType), ",")), + ) + } + + svc := desc.NewObject() + if err := resManager.Get(request.Request.Context(), svc, store.GetByKey(svcName, svcMesh)); err != nil { + rest_errors.HandleError(request.Request.Context(), response, err, "could not retrieve service") + return + } + + hostnameGenerators := hostnamegenerator_api.HostnameGeneratorResourceList{} + if err := resManager.List(request.Request.Context(), &hostnameGenerators); err != nil { + rest_errors.HandleError(request.Request.Context(), response, err, "could not retrieve hostname generators") + return + } + + byHostname := map[string][]types.InspectHostnameZone{} + + for _, hg := range hostnameGenerators.Items { + svcZone := model.ZoneOfResource(svc) + hgZone := model.ZoneOfResource(hg) + hgOrigin, _ := model.ResourceOrigin(hg.GetMeta()) + + // rewrite origin to simulate matching from a perspective of a single zone + var origin mesh_proto.ResourceOrigin + if hgOrigin == mesh_proto.ZoneResourceOrigin && hgZone == svcZone { + origin = mesh_proto.ZoneResourceOrigin + } else { + origin = mesh_proto.GlobalResourceOrigin + } + overridden := ResourceMetaWithOverriddenOrigin{ + ResourceMeta: svc.GetMeta(), + origin: origin, + } + svc.SetMeta(overridden) + + host, err := generatorsForType[svcType].GenerateHostname(svcZone, hg, svc) + if err != nil { + rest_errors.HandleError(request.Request.Context(), response, err, "could not generate hostname") + return + } + if host == "" { + // hostname generator did not match the service + continue + } + byHostname[host] = append(byHostname[host], types.InspectHostnameZone{ + HostnameGeneratorCoreName: hg.GetMeta().GetName(), + Name: svcZone, + }) + } + + resp := types.InspectHostnames{} + for _, host := range util_maps.SortedKeys(byHostname) { + zones := byHostname[host] + sort.Slice(zones, func(i, j int) bool { + return zones[i].Name < zones[j].Name + }) + resp.Items = append(resp.Items, types.InspectHostname{ + Hostname: host, + Zones: zones, + }) + } + resp.Total = len(resp.Items) + if err := response.WriteAsJson(resp); err != nil { + rest_errors.HandleError(request.Request.Context(), response, err, "Failed writing response") + } + } +} + +type ResourceMetaWithOverriddenOrigin struct { + model.ResourceMeta + origin mesh_proto.ResourceOrigin +} + +func (r ResourceMetaWithOverriddenOrigin) GetLabels() map[string]string { + cloned := maps.Clone(r.ResourceMeta.GetLabels()) + if cloned == nil { + cloned = map[string]string{} + } + cloned[mesh_proto.ResourceOriginLabel] = string(r.origin) + return cloned +} diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshexternalservice_hostnames_global.golden.json b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshexternalservice_hostnames_global.golden.json new file mode 100644 index 000000000000..200404d85aa6 --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshexternalservice_hostnames_global.golden.json @@ -0,0 +1,14 @@ +{ + "items": [ + { + "hostname": "mes-1.extsvc.mesh.local", + "zones": [ + { + "hostnameGeneratorCoreName": "synced-meshexternalservice", + "name": "mes-1" + } + ] + } + ], + "total": 1 +} diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshexternalservice_hostnames_global.input.yaml b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshexternalservice_hostnames_global.input.yaml new file mode 100644 index 000000000000..f5a3b75fc393 --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshexternalservice_hostnames_global.input.yaml @@ -0,0 +1,26 @@ +#/meshes/default/meshexternalservices/mes-1/_resources/hostnames 200 +type: Mesh +name: default +--- +type: MeshExternalService +name: mes-1 +mesh: default +spec: + match: + type: HostnameGenerator + port: 9090 + protocol: http + endpoints: + - address: 127.0.0.1 + port: 8080 +--- +type: HostnameGenerator +name: synced-meshexternalservice +labels: + kuma.io/origin: global +spec: + template: '{{ .DisplayName }}.extsvc.mesh.local' + selector: + meshExternalService: + matchLabels: + kuma.io/origin: global diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshmultizoneservice_hostnames_global.golden.json b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshmultizoneservice_hostnames_global.golden.json new file mode 100644 index 000000000000..3e0361c8ca36 --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshmultizoneservice_hostnames_global.golden.json @@ -0,0 +1,14 @@ +{ + "items": [ + { + "hostname": "test-server.mzsvc.mesh.local", + "zones": [ + { + "hostnameGeneratorCoreName": "synced-meshmultizoneservice", + "name": "test-server" + } + ] + } + ], + "total": 1 +} diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshmultizoneservice_hostnames_global.input.yaml b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshmultizoneservice_hostnames_global.input.yaml new file mode 100644 index 000000000000..7410522ab5bd --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshmultizoneservice_hostnames_global.input.yaml @@ -0,0 +1,26 @@ +#/meshes/default/meshmultizoneservices/test-server/_resources/hostnames 200 +type: Mesh +name: default +--- +type: MeshMultiZoneService +name: test-server +mesh: default +spec: + selector: + meshService: + matchLabels: + kuma.io/display-name: test-server + ports: + - port: 80 + appProtocol: http +--- +type: HostnameGenerator +name: synced-meshmultizoneservice +labels: + kuma.io/origin: global +spec: + template: '{{ .DisplayName }}.mzsvc.mesh.local' + selector: + meshMultiZoneService: + matchLabels: + kuma.io/origin: global diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_global.golden.json b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_global.golden.json new file mode 100644 index 000000000000..6fee5f5f8008 --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_global.golden.json @@ -0,0 +1,14 @@ +{ + "items": [ + { + "hostname": "test-server.svc.west.mesh.local", + "zones": [ + { + "hostnameGeneratorCoreName": "synced-mesh-services", + "name": "west" + } + ] + } + ], + "total": 1 +} diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_global.input.yaml b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_global.input.yaml new file mode 100644 index 000000000000..24c6d0c536b5 --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_global.input.yaml @@ -0,0 +1,33 @@ +#/meshes/default/meshservices/test-server-syncedhash/_resources/hostnames 200 +type: Mesh +name: default +--- +type: MeshService +name: test-server-syncedhash +mesh: default +labels: + kuma.io/origin: zone + kuma.io/env: universal + kuma.io/display-name: test-server + kuma.io/zone: west +spec: + selector: + dataplaneTags: + kuma.io/service: test-server + ports: + - port: 80 + targetPort: 80 + appProtocol: http + name: main-port +--- +type: HostnameGenerator +name: synced-mesh-services +labels: + kuma.io/origin: global +spec: + template: '{{ .DisplayName }}.svc.{{ .Zone }}.mesh.local' + selector: + meshService: + matchLabels: + kuma.io/origin: global + kuma.io/env: universal diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_local.golden.json b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_local.golden.json new file mode 100644 index 000000000000..41e49f867ff1 --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_local.golden.json @@ -0,0 +1,14 @@ +{ + "items": [ + { + "hostname": "test-server.svc.mesh.local", + "zones": [ + { + "hostnameGeneratorCoreName": "local-mesh-services", + "name": "east" + } + ] + } + ], + "total": 1 +} diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_local.input.yaml b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_local.input.yaml new file mode 100644 index 000000000000..5aa9549b3fd7 --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_local.input.yaml @@ -0,0 +1,34 @@ +#/meshes/default/meshservices/test-server/_resources/hostnames 200 +type: Mesh +name: default +--- +type: MeshService +name: test-server +mesh: default +labels: + kuma.io/origin: zone + kuma.io/env: universal + kuma.io/display-name: test-server + kuma.io/zone: east +spec: + selector: + dataplaneTags: + kuma.io/service: test-server + ports: + - port: 80 + targetPort: 80 + appProtocol: http + name: main-port +--- +type: HostnameGenerator +name: local-mesh-services +labels: + kuma.io/origin: zone + kuma.io/zone: east +spec: + template: '{{ .DisplayName }}.svc.mesh.local' + selector: + meshService: + matchLabels: + kuma.io/origin: zone + kuma.io/env: universal diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_synced.golden.json b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_synced.golden.json new file mode 100644 index 000000000000..17f39f886b94 --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_synced.golden.json @@ -0,0 +1,14 @@ +{ + "items": [ + { + "hostname": "test-server.svc.west.mesh.local", + "zones": [ + { + "hostnameGeneratorCoreName": "synced-mesh-services-syncedhash", + "name": "west" + } + ] + } + ], + "total": 1 +} diff --git a/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_synced.input.yaml b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_synced.input.yaml new file mode 100644 index 000000000000..991f32cd3fc8 --- /dev/null +++ b/pkg/api-server/testdata/resources/inspect/services/_resources/hostnames/meshservice_hostnames_zone_synced.input.yaml @@ -0,0 +1,33 @@ +#/meshes/default/meshservices/test-server-syncedhash/_resources/hostnames 200 +type: Mesh +name: default +--- +type: MeshService +name: test-server-syncedhash +mesh: default +labels: + kuma.io/origin: global + kuma.io/env: universal + kuma.io/display-name: test-server + kuma.io/zone: west +spec: + selector: + dataplaneTags: + kuma.io/service: test-server + ports: + - port: 80 + targetPort: 80 + appProtocol: http + name: main-port +--- +type: HostnameGenerator +name: synced-mesh-services-syncedhash +labels: + kuma.io/origin: global +spec: + template: '{{ .DisplayName }}.svc.{{ .Zone }}.mesh.local' + selector: + meshService: + matchLabels: + kuma.io/origin: global + kuma.io/env: universal