diff --git a/pkg/core/resources/apis/meshservice/api/v1alpha1/helpers.go b/pkg/core/resources/apis/meshservice/api/v1alpha1/helpers.go index ffdcc135153d..fcdced1b8fdf 100644 --- a/pkg/core/resources/apis/meshservice/api/v1alpha1/helpers.go +++ b/pkg/core/resources/apis/meshservice/api/v1alpha1/helpers.go @@ -43,15 +43,15 @@ func (m *MeshServiceResource) FindPortByName(name string) (Port, bool) { return Port{}, false } -func (m *MeshServiceResource) IsLocalMeshService(localZone string) bool { +func (m *MeshServiceResource) IsLocalMeshService() bool { if len(m.GetMeta().GetLabels()) == 0 { return true // no labels mean that it's a local resource } - resZone, ok := m.GetMeta().GetLabels()[mesh_proto.ZoneTag] + origin, ok := m.GetMeta().GetLabels()[mesh_proto.ResourceOriginLabel] if !ok { return true // no zone label mean that it's a local resource } - return resZone == localZone + return origin == string(mesh_proto.ZoneResourceOrigin) } var _ core_vip.ResourceHoldingVIPs = &MeshServiceResource{} diff --git a/pkg/core/resources/apis/meshservice/status/updater.go b/pkg/core/resources/apis/meshservice/status/updater.go index 0d2e88f9f5a7..7b3ac51722d3 100644 --- a/pkg/core/resources/apis/meshservice/status/updater.go +++ b/pkg/core/resources/apis/meshservice/status/updater.go @@ -112,7 +112,7 @@ func (s *StatusUpdater) updateStatus(ctx context.Context) error { dppsForMs := meshservice.MatchDataplanesWithMeshServices(dpList.Items, msList.Items, false) for ms, dpps := range dppsForMs { - if !ms.IsLocalMeshService(s.localZone) { + if !ms.IsLocalMeshService() { // identities are already computed by the other zone continue } diff --git a/pkg/core/resources/apis/meshservice/status/updater_test.go b/pkg/core/resources/apis/meshservice/status/updater_test.go index 2d53802a52dd..b0d85abc4018 100644 --- a/pkg/core/resources/apis/meshservice/status/updater_test.go +++ b/pkg/core/resources/apis/meshservice/status/updater_test.go @@ -72,7 +72,8 @@ var _ = Describe("Updater", func() { // when Expect(samples.MeshServiceBackendBuilder(). WithLabels(map[string]string{ - v1alpha1.ZoneTag: "west", + v1alpha1.ZoneTag: "west", + v1alpha1.ResourceOriginLabel: string(v1alpha1.GlobalResourceOrigin), }). AddServiceTagIdentity("backend"). Create(resManager)).To(Succeed()) diff --git a/pkg/kds/context/context.go b/pkg/kds/context/context.go index 0f5301b2b110..ab38f2d34b08 100644 --- a/pkg/kds/context/context.go +++ b/pkg/kds/context/context.go @@ -103,6 +103,7 @@ func DefaultContext( util.WithLabel(mesh_proto.ResourceOriginLabel, string(mesh_proto.ZoneResourceOrigin)), util.WithLabel(mesh_proto.ZoneTag, cfg.Multizone.Zone.Name), util.WithoutLabel(mesh_proto.DeletionGracePeriodStartedLabel), + util.If(util.IsKubernetes(cfg.Store.Type), util.PopulateNamespaceLabelFromNameExtension()), ), MapInsightResourcesZeroGeneration, reconcile_v2.If( diff --git a/pkg/kds/util/meta.go b/pkg/kds/util/meta.go index 0a66c506a778..3f61761ee239 100644 --- a/pkg/kds/util/meta.go +++ b/pkg/kds/util/meta.go @@ -6,15 +6,17 @@ import ( "golang.org/x/exp/maps" mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1" + config_store "github.com/kumahq/kuma/pkg/config/core/resources/store" "github.com/kumahq/kuma/pkg/core/resources/model" ) // KDS ResourceMeta only contains name and mesh. // The rest is managed by the receiver of resources anyways. See ResourceSyncer#Sync type resourceMeta struct { - name string - mesh string - labels map[string]string + name string + mesh string + labels map[string]string + nameExtensions model.ResourceNameExtensions } type CloneResourceMetaOpt func(*resourceMeta) @@ -34,21 +36,57 @@ func WithLabel(key, value string) CloneResourceMetaOpt { } } +// PopulateNamespaceLabelFromNameExtension on Kubernetes zones adds 'k8s.kuma.io/namespace' label to the resources +// before syncing them to Global. +// +// In 2.7.x method 'GetMeta().GetLabels()' on Kubernetes returned a label map with 'k8s.kuma.io/namespace' added +// dynamically. This behavior was changed in 2.9.x by https://github.com/kumahq/kuma/pull/11020, the namespace label is now +// supposed to be set in ComputeLabels function. But this functions is called only on Create/Update of the resources. +// This means policies that were created on 2.7.x won't have 'k8s.kuma.io/namespace' label when synced to Global. +// Even though the lack of namespace labels affects only how resource looks in GUI on Global it's still worth setting it. +func PopulateNamespaceLabelFromNameExtension() CloneResourceMetaOpt { + return func(m *resourceMeta) { + namespace := m.nameExtensions[model.K8sNamespaceComponent] + if _, ok := m.labels[mesh_proto.KubeNamespaceTag]; !ok && namespace != "" { + m.labels[mesh_proto.KubeNamespaceTag] = namespace + } + } +} + func WithoutLabel(key string) CloneResourceMetaOpt { return func(m *resourceMeta) { delete(m.labels, key) } } +func If(condition func(resource model.ResourceMeta) bool, fn CloneResourceMetaOpt) CloneResourceMetaOpt { + return func(meta *resourceMeta) { + if condition(meta) { + fn(meta) + } + } +} + +func IsKubernetes(storeType config_store.StoreType) func(model.ResourceMeta) bool { + return func(_ model.ResourceMeta) bool { + return storeType == config_store.KubernetesStore + } +} + func CloneResourceMeta(m model.ResourceMeta, fs ...CloneResourceMetaOpt) model.ResourceMeta { labels := maps.Clone(m.GetLabels()) if labels == nil { labels = map[string]string{} } + ne := maps.Clone(m.GetNameExtensions()) + if ne == nil { + ne = model.ResourceNameExtensions{} + } meta := &resourceMeta{ - name: m.GetName(), - mesh: m.GetMesh(), - labels: labels, + name: m.GetName(), + mesh: m.GetMesh(), + labels: labels, + nameExtensions: ne, } for _, f := range fs { f(meta) diff --git a/pkg/plugins/policies/core/xds/meshroute/clusters.go b/pkg/plugins/policies/core/xds/meshroute/clusters.go index ed582fafc9ab..190f8520cbad 100644 --- a/pkg/plugins/policies/core/xds/meshroute/clusters.go +++ b/pkg/plugins/policies/core/xds/meshroute/clusters.go @@ -96,9 +96,12 @@ func GenerateClusters( } } else { if realResourceRef := service.BackendRef().RealResourceBackendRef(); realResourceRef != nil { + tlsReady = true // tls readiness is only relevant for MeshService if common_api.TargetRefKind(realResourceRef.Resource.ResourceType) == common_api.MeshService { if ms := meshCtx.MeshServiceByIdentifier[pointer.Deref(realResourceRef.Resource).ResourceIdentifier]; ms != nil { - tlsReady = ms.Status.TLS.Status == meshservice_api.TLSReady + // we only check TLS status for local service + // services that are synced can be accessed only with TLS through ZoneIngress + tlsReady = !ms.IsLocalMeshService() || ms.Status.TLS.Status == meshservice_api.TLSReady } } edsClusterBuilder.Configure(envoy_clusters.ClientSideMultiIdentitiesMTLS( diff --git a/pkg/xds/topology/outbound.go b/pkg/xds/topology/outbound.go index 1d2ab43558c0..cce127ea64bd 100644 --- a/pkg/xds/topology/outbound.go +++ b/pkg/xds/topology/outbound.go @@ -130,7 +130,7 @@ func BuildEdsEndpointMap( fillExternalServicesOutboundsThroughEgress(outbound, externalServices, meshExternalServices, zoneEgresses, mesh, localZone) // it has to be last because it reuses endpoints for other cases - fillMeshMultiZoneServices(outbound, meshServicesByName, meshMultiZoneServices, localZone) + fillMeshMultiZoneServices(outbound, meshServicesByName, meshMultiZoneServices) return outbound } @@ -139,7 +139,6 @@ func fillMeshMultiZoneServices( outbound core_xds.EndpointMap, meshServicesByName map[model.ResourceIdentifier]*meshservice_api.MeshServiceResource, meshMultiZoneServices []*meshmzservice_api.MeshMultiZoneServiceResource, - localZone string, ) { for _, mzSvc := range meshMultiZoneServices { for _, matchedMs := range mzSvc.Status.MeshServices { @@ -153,7 +152,7 @@ func fillMeshMultiZoneServices( if !ok { continue } - if !ms.IsLocalMeshService(localZone) && ms.Spec.State != meshservice_api.StateAvailable { + if !ms.IsLocalMeshService() && ms.Spec.State != meshservice_api.StateAvailable { // we don't want to load balance to zones that has no available endpoints. // we check this only for non-local services, because if service is unavailable in the local zone it has no endpoints. // if a new local endpoint just become healthy, we can add it immediately without waiting for state to be reconciled. @@ -215,7 +214,7 @@ func fillRemoteMeshServices( } for _, ms := range services { - if ms.IsLocalMeshService(localZone) { + if ms.IsLocalMeshService() { continue } msZone := ms.GetMeta().GetLabels()[mesh_proto.ZoneTag] @@ -328,7 +327,7 @@ func fillLocalMeshServices( ) { dppsForMs := meshservice.MatchDataplanesWithMeshServices(dataplanes, meshServices, true) for meshSvc, dpps := range dppsForMs { - if !meshSvc.IsLocalMeshService(localZone) { + if !meshSvc.IsLocalMeshService() { continue } diff --git a/test/e2e_env/multizone/meshservice/connectivity.go b/test/e2e_env/multizone/meshservice/connectivity.go index c79d8c89ff31..e788f482e776 100644 --- a/test/e2e_env/multizone/meshservice/connectivity.go +++ b/test/e2e_env/multizone/meshservice/connectivity.go @@ -9,7 +9,9 @@ import ( "github.com/onsi/gomega/types" kube_meta "k8s.io/apimachinery/pkg/apis/meta/v1" + mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1" "github.com/kumahq/kuma/pkg/config/core" + "github.com/kumahq/kuma/pkg/test/resources/samples" "github.com/kumahq/kuma/test/e2e_env/kubernetes/gateway" . "github.com/kumahq/kuma/test/framework" "github.com/kumahq/kuma/test/framework/client" @@ -29,7 +31,11 @@ func Connectivity() { var testServerPodNames []string BeforeAll(func() { Expect(NewClusterSetup(). - Install(MTLSMeshWithMeshServicesUniversal(meshName, "Everywhere")). + Install(Yaml(samples.MeshMTLSBuilder(). + WithName(meshName). + WithMeshServicesEnabled(mesh_proto.Mesh_MeshServices_Everywhere). + WithPermissiveMTLSBackends(), + )). Install(MeshTrafficPermissionAllowAllUniversal(meshName)). Install(YamlUniversal(fmt.Sprintf(` type: HostnameGenerator