Repository: istio/istio (go)

Issue:
[release-1.12] enable multicluster headless by default
Manual cherrypick required.

#35883 failed to apply on top of branch "release-1.12":
```
Applying: enable multicluster headless by default
Using index info to reconstruct a base tree...
M	pilot/pkg/features/pilot.go
M	tests/integration/iop-integration-test-defaults-with-quic.yaml
M	tests/integration/iop-integration-test-defaults.yaml
Falling back to patching base and 3-way merge...
Auto-merging tests/integration/iop-integration-test-defaults.yaml
CONFLICT (content): Merge conflict in tests/integration/iop-integration-test-defaults.yaml
Auto-merging tests/integration/iop-integration-test-defaults-with-quic.yaml
CONFLICT (content): Merge conflict in tests/integration/iop-integration-test-defaults-with-quic.yaml
Auto-merging pilot/pkg/features/pilot.go
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 enable multicluster headless by default
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

```

Pull request description:
enable multicluster headless by default (#35883)
fixes #35891 

Patch:
diff --git a/pilot/pkg/features/pilot.go b/pilot/pkg/features/pilot.go
index 08735d4a88..9690de8c1a 100644
--- a/pilot/pkg/features/pilot.go
+++ b/pilot/pkg/features/pilot.go
@@ -536,7 +536,7 @@ var (
 	EnableRouteCollapse = env.RegisterBoolVar("PILOT_ENABLE_ROUTE_COLLAPSE_OPTIMIZATION", true,
 		"If true, Pilot will merge virtual hosts with the same routes into a single virtual host, as an optimization.").Get()
 
-	MulticlusterHeadlessEnabled = env.RegisterBoolVar("ENABLE_MULTICLUSTER_HEADLESS", false,
+	MulticlusterHeadlessEnabled = env.RegisterBoolVar("ENABLE_MULTICLUSTER_HEADLESS", true,
 		"If true, the DNS name table for a headless service will resolve to same-network endpoints in any cluster.").Get()
 
 	CertSignerDomain = env.RegisterStringVar("CERT_SIGNER_DOMAIN", "", "The cert signer domain info").Get()
diff --git a/pilot/pkg/model/push_context.go b/pilot/pkg/model/push_context.go
index 0f68e12752..6567ae983f 100644
--- a/pilot/pkg/model/push_context.go
+++ b/pilot/pkg/model/push_context.go
@@ -383,6 +383,8 @@ const (
 	ProxyRequest TriggerReason = "proxyrequest"
 	// NamespaceUpdate describes a push triggered by a Namespace change
 	NamespaceUpdate TriggerReason = "namespace"
+	// ClusterUpdate describes a push triggered by a Cluster change
+	ClusterUpdate TriggerReason = "cluster"
 )
 
 // Merge two update requests together
diff --git a/pilot/pkg/serviceregistry/kube/controller/controller.go b/pilot/pkg/serviceregistry/kube/controller/controller.go
index 5a5d979367..446839c396 100644
--- a/pilot/pkg/serviceregistry/kube/controller/controller.go
+++ b/pilot/pkg/serviceregistry/kube/controller/controller.go
@@ -656,7 +656,8 @@ func (c *Controller) onNodeEvent(obj interface{}, event model.Event) error {
 	// update all related services
 	if updatedNeeded && c.updateServiceNodePortAddresses() {
 		c.opts.XDSUpdater.ConfigUpdate(&model.PushRequest{
-			Full: true,
+			Full:   true,
+			Reason: []model.TriggerReason{model.ServiceUpdate},
 		})
 	}
 	return nil
diff --git a/pilot/pkg/serviceregistry/kube/controller/multicluster.go b/pilot/pkg/serviceregistry/kube/controller/multicluster.go
index 5b6b78746c..42406859de 100644
--- a/pilot/pkg/serviceregistry/kube/controller/multicluster.go
+++ b/pilot/pkg/serviceregistry/kube/controller/multicluster.go
@@ -309,7 +309,7 @@ func (m *Multicluster) ClusterDeleted(clusterID cluster.ID) error {
 	}
 	delete(m.remoteKubeControllers, clusterID)
 	if m.XDSUpdater != nil {
-		m.XDSUpdater.ConfigUpdate(&model.PushRequest{Full: true})
+		m.XDSUpdater.ConfigUpdate(&model.PushRequest{Full: true, Reason: []model.TriggerReason{model.ClusterUpdate}})
 	}
 
 	return nil
diff --git a/pilot/pkg/xds/monitoring.go b/pilot/pkg/xds/monitoring.go
index bbc5728973..409a2d79e7 100644
--- a/pilot/pkg/xds/monitoring.go
+++ b/pilot/pkg/xds/monitoring.go
@@ -215,6 +215,7 @@ var triggerMetric = map[model.TriggerReason]monitoring.Metric{
 	model.NetworksTrigger: pushTriggers.With(typeTag.Value(string(model.NetworksTrigger))),
 	model.ProxyRequest:    pushTriggers.With(typeTag.Value(string(model.ProxyRequest))),
 	model.NamespaceUpdate: pushTriggers.With(typeTag.Value(string(model.NamespaceUpdate))),
+	model.ClusterUpdate:   pushTriggers.With(typeTag.Value(string(model.ClusterUpdate))),
 }
 
 func recordPushTriggers(reasons ...model.TriggerReason) {
diff --git a/releasenotes/notes/35883.yaml b/releasenotes/notes/35883.yaml
index 521b50381d..5c771c7c23 100644
--- a/releasenotes/notes/35883.yaml
+++ b/releasenotes/notes/35883.yaml
@@ -1,10 +1,8 @@
 apiVersion: release-notes/v2
 kind: feature
-area: istioctl
-issue:
-  - 18487
-
+area: traffic-management
 releaseNotes:
 - |
-  **Updated** `WorkloadEntry` resources will be read across clusters and no longer need to be manually mirrored to other
-  clusters. These endpoints are now automatically discovered and reachable in both multi-cluster and multi-network meshes.
+  **Enabled** discovering cross-cluster, same network, endpoints for headless services when `ISTIO_META_DNS_CAPTURE` and
+  `ISTIO_META_DNS_AUTO_ALLOCATE` are enabled in `ProxyConfig.proxyMetadata`. This can be disabled by setting
+  `ENABLE_MULTICLUSTER_HEADLESS=false` in istiod.
diff --git a/releasenotes/notes/35884.yaml b/releasenotes/notes/35884.yaml
new file mode 100644
index 0000000000..2d0ad21c6a
--- /dev/null
+++ b/releasenotes/notes/35884.yaml
@@ -0,0 +1,7 @@
+apiVersion: release-notes/v2
+kind: feature
+area: traffic-management
+releaseNotes:
+  - |
+    **Updated** `WorkloadEntry` resources will be read across clusters and no longer need to be manually mirrored to other
+    clusters. These endpoints are now automatically discovered and reachable in both multi-cluster and multi-network meshes.
\ No newline at end of file


Test patch:
diff --git a/tests/integration/iop-integration-test-defaults-with-quic.yaml b/tests/integration/iop-integration-test-defaults-with-quic.yaml
index 0b9be45b90..f1914677f0 100644
--- a/tests/integration/iop-integration-test-defaults-with-quic.yaml
+++ b/tests/integration/iop-integration-test-defaults-with-quic.yaml
@@ -60,7 +60,6 @@ spec:
       autoscaleEnabled: false
       env:
         UNSAFE_ENABLE_ADMIN_ENDPOINTS: true
-        ENABLE_MULTICLUSTER_HEADLESS: true
         PILOT_REMOTE_CLUSTER_TIMEOUT: 15s
         PILOT_ENABLE_QUIC_LISTENERS: true
 
diff --git a/tests/integration/iop-integration-test-defaults.yaml b/tests/integration/iop-integration-test-defaults.yaml
index fc24cf686e..a0ff776487 100644
--- a/tests/integration/iop-integration-test-defaults.yaml
+++ b/tests/integration/iop-integration-test-defaults.yaml
@@ -56,7 +56,6 @@ spec:
       autoscaleEnabled: false
       env:
         UNSAFE_ENABLE_ADMIN_ENDPOINTS: true
-        ENABLE_MULTICLUSTER_HEADLESS: true
         PILOT_REMOTE_CLUSTER_TIMEOUT: 15s
 
     gateways: