Repository: linkerd/linkerd2
Pull request #8045 (2022-03-11 03:09:43)

build(deps): bump github.com/spf13/cobra from 1.3.0 to 1.4.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.3.0 to 1.4.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/spf13/cobra/releases">github.com/spf13/cobra's releases</a>.</em></p>
<blockquote>
<h2>v1.4.0</h2>
<h1>Winter 2022 Release ❄️</h1>
<p>Another season, another release!</p>
<h2>Goodbye viper! 🐍 🚀</h2>
<p>The core Cobra library no longer requires Viper and all of its indirect dependencies. This means that Cobra's dependency tree has been drastically thinned! The Viper dependency was included because of the <code>cobra</code> CLI generation tool. <a href="https://github.com/spf13/cobra-cli/releases/tag/v1.3.0">This tool has migrated to <code>spf13/cobra-cli</code></a>.</p>
<p>It's <em>pretty unlikely</em> you were importing and using <strong>the bootstrapping CLI tool</strong> as part of your application (after all, it's just a tool to get going with core <code>cobra</code>).</p>
<p>But if you were, replace occurrences of</p>
<pre><code>&quot;github.com/spf13/cobra/cobra&quot;
</code></pre>
<p>with</p>
<pre><code>&quot;github.com/spf13/cobra-cli&quot;
</code></pre>
<p>And in your <code>go.mod</code>, you'll want to also include this dependency:</p>
<pre><code>github.com/spf13/cobra-cli v1.3.0
</code></pre>
<p>Again, the maintainers <em>do not anticipate</em> this being a breaking change to users of the core <code>cobra</code> library, so minimal work should be required for users to integrate with this new release. Moreover, this means the dependency tree for your application using Cobra should no longer require dependencies that were inherited from Viper. Huzzah! 🥳</p>
<p>If you'd like to read more</p>
<ul>
<li>issue: <a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1597">spf13/cobra#1597</a></li>
<li>PR: <a href="https://github-redirect.dependabot.com/spf13/cobra/pull/1604">spf13/cobra#1604</a></li>
</ul>
<h2>Documentation 📝</h2>
<ul>
<li>Update Go Doc link and badge in README: <a href="https://github-redirect.dependabot.com/spf13/cobra/pull/1593">spf13/cobra#1593</a></li>
<li>Fix to install command, now targets <code>@latest</code>: <a href="https://github-redirect.dependabot.com/spf13/cobra/pull/1576">spf13/cobra#1576</a></li>
<li>Added MAINTAINERS file: <a href="https://github-redirect.dependabot.com/spf13/cobra/pull/1545">spf13/cobra#1545</a></li>
</ul>
<h2>Other 💭</h2>
<ul>
<li>Bumped license year to 2022 in golden files: <a href="https://github-redirect.dependabot.com/spf13/cobra/pull/1575">spf13/cobra#1575</a></li>
<li>Added Pixie to projects: <a href="https://github-redirect.dependabot.com/spf13/cobra/pull/1581">spf13/cobra#1581</a></li>
<li>Updated labeler for new labeling scheme: <a href="https://github-redirect.dependabot.com/spf13/cobra/pull/1613">spf13/cobra#1613</a> &amp; syntax fix: <a href="https://github-redirect.dependabot.com/spf13/cobra/pull/1624">spf13/cobra#1624</a></li>
</ul>
<p>Shoutout to our awesome contributors helping to make this cobra release possible!!
<a href="https://github.com/spf13"><code>@​spf13</code></a> <a href="https://github.com/marckhouzam"><code>@​marckhouzam</code></a> <a href="https://github.com/johnSchnake"><code>@​johnSchnake</code></a> <a href="https://github.com/jpmcb"><code>@​jpmcb</code></a> <a href="https://github.com/liggitt"><code>@​liggitt</code></a> <a href="https://github.com/umarcor"><code>@​umarcor</code></a> <a href="https://github.com/hiljusti"><code>@​hiljusti</code></a> <a href="https://github.com/marians"><code>@​marians</code></a> <a href="https://github.com/shyim"><code>@​shyim</code></a> <a href="https://github.com/htroisi"><code>@​htroisi</code></a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/spf13/cobra/blob/master/CHANGELOG.md">github.com/spf13/cobra's changelog</a>.</em></p>
<blockquote>
<h1>Cobra Changelog</h1>
<h2>v1.1.3</h2>
<ul>
<li><strong>Fix:</strong> release-branch.cobra1.1 only: Revert &quot;Deprecate Go &lt; 1.14&quot; to maintain backward compatibility</li>
</ul>
<h2>v1.1.2</h2>
<h3>Notable Changes</h3>
<ul>
<li>Bump license year to 2021 in golden files (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1309">#1309</a>) <a href="https://github.com/Bowbaq"><code>@​Bowbaq</code></a></li>
<li>Enhance PowerShell completion with custom comp (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1208">#1208</a>) <a href="https://github.com/Luap99"><code>@​Luap99</code></a></li>
<li>Update gopkg.in/yaml.v2 to v2.4.0: The previous breaking change in yaml.v2 v2.3.0 has been reverted, see <a href="https://github-redirect.dependabot.com/go-yaml/yaml/issues/670">go-yaml/yaml#670</a></li>
<li>Documentation readability improvements (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1228">#1228</a> etc.) <a href="https://github.com/zaataylor"><code>@​zaataylor</code></a> etc.</li>
<li>Use golangci-lint: Repair warnings and errors resulting from linting (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1044">#1044</a>) <a href="https://github.com/umarcor"><code>@​umarcor</code></a></li>
</ul>
<h2>v1.1.1</h2>
<ul>
<li><strong>Fix:</strong> yaml.v2 2.3.0 contained a unintended breaking change. This release reverts to yaml.v2 v2.2.8 which has recent critical CVE fixes, but does not have the breaking changes. See <a href="https://github-redirect.dependabot.com/spf13/cobra/pull/1259">spf13/cobra#1259</a> for context.</li>
<li><strong>Fix:</strong> correct internal formatting for go-md2man v2 (which caused man page generation to be broken). See <a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1049">spf13/cobra#1049</a> for context.</li>
</ul>
<h2>v1.1.0</h2>
<h3>Notable Changes</h3>
<ul>
<li>Extend Go completions and revamp zsh comp (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1070">#1070</a>)</li>
<li>Fix man page doc generation - no auto generated tag when <code>cmd.DisableAutoGenTag = true</code> (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1104">#1104</a>) <a href="https://github.com/jpmcb"><code>@​jpmcb</code></a></li>
<li>Add completion for help command (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1136">#1136</a>)</li>
<li>Complete subcommands when TraverseChildren is set (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1171">#1171</a>)</li>
<li>Fix stderr printing functions (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/894">#894</a>)</li>
<li>fix: fish output redirection (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1247">#1247</a>)</li>
</ul>
<h2>v1.0.0</h2>
<p>Announcing v1.0.0 of Cobra. 🎉</p>
<h3>Notable Changes</h3>
<ul>
<li>Fish completion (including support for Go custom completion) <a href="https://github.com/marckhouzam"><code>@​marckhouzam</code></a></li>
<li>API (urgent): Rename BashCompDirectives to ShellCompDirectives <a href="https://github.com/marckhouzam"><code>@​marckhouzam</code></a></li>
<li>Remove/replace SetOutput on Command - deprecated <a href="https://github.com/jpmcb"><code>@​jpmcb</code></a></li>
<li>add support for autolabel stale PR <a href="https://github.com/xchapter7x"><code>@​xchapter7x</code></a></li>
<li>Add Labeler Actions <a href="https://github.com/xchapter7x"><code>@​xchapter7x</code></a></li>
<li>Custom completions coded in Go (instead of Bash) <a href="https://github.com/marckhouzam"><code>@​marckhouzam</code></a></li>
<li>Partial Revert of <a href="https://github-redirect.dependabot.com/spf13/cobra/issues/922">#922</a> <a href="https://github.com/jharshman"><code>@​jharshman</code></a></li>
<li>Add Makefile to project <a href="https://github.com/jharshman"><code>@​jharshman</code></a></li>
<li>Correct documentation for InOrStdin <a href="https://github.com/desponda"><code>@​desponda</code></a></li>
<li>Apply formatting to templates <a href="https://github.com/jharshman"><code>@​jharshman</code></a></li>
<li>Revert change so help is printed on stdout again <a href="https://github.com/marckhouzam"><code>@​marckhouzam</code></a></li>
<li>Update md2man to v2.0.0 <a href="https://github.com/pdf"><code>@​pdf</code></a></li>
<li>update viper to v1.4.0 <a href="https://github.com/umarcor"><code>@​umarcor</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/spf13/cobra/commit/5b2b9e9f61d36ccb66167301f76a2292c3729855"><code>5b2b9e9</code></a> Removes viper dependency by removing cobra/ CLI tool (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1604">#1604</a>)</li>
<li><a href="https://github.com/spf13/cobra/commit/9369465955fcc766aed3e7cf574277ee553251d4"><code>9369465</code></a> labeler: fix syntax</li>
<li><a href="https://github.com/spf13/cobra/commit/e1b831e5c2a1639f3bba57d0922fd0c7a7816df7"><code>e1b831e</code></a> Updates labeler for new labels (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1613">#1613</a>)</li>
<li><a href="https://github.com/spf13/cobra/commit/8267283cfe84768a0ec01b29b981f37e2ea99299"><code>8267283</code></a> Add MAINTAINERS file (alphabetical order) (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1545">#1545</a>)</li>
<li><a href="https://github.com/spf13/cobra/commit/6d2dc43606da38b673801b8f678c7e5143dfad58"><code>6d2dc43</code></a> Add Pixie to projects. (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1581">#1581</a>)</li>
<li><a href="https://github.com/spf13/cobra/commit/a5996323794c96a0d14df49f6675708a87e53b98"><code>a599632</code></a> Fix install command (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1576">#1576</a>)</li>
<li><a href="https://github.com/spf13/cobra/commit/7cabfeb8f837c7bd5b834f4fbe98fdef1136465a"><code>7cabfeb</code></a> Update Go Doc link and badge (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1593">#1593</a>)</li>
<li><a href="https://github.com/spf13/cobra/commit/e04ec725508c760e70263b031e5697c232d5c3fa"><code>e04ec72</code></a> Bump license year to 2022 in golden files (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1575">#1575</a>)</li>
<li><a href="https://github.com/spf13/cobra/commit/cb9d7b1cec87c2bb005c6e2790553bcd629bc542"><code>cb9d7b1</code></a> Bump github.com/spf13/viper from 1.10.0 to 1.10.1 (<a href="https://github-redirect.dependabot.com/spf13/cobra/issues/1567">#1567</a>)</li>
<li>See full diff in <a href="https://github.com/spf13/cobra/compare/v1.3.0...v1.4.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/spf13/cobra&package-manager=go_modules&previous-version=1.3.0&new-version=1.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 54230ed65..a1e9c2ca7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,20 @@
 # Changes
 
+## edge-22.3.2
+
+This edge release includes a few fixes and quality of life improvements. An
+issue has been fixed in the proxy allowing HTTP Upgrade requests to work
+through multi-cluster gateways, and the init container's resource limits and
+requests have been revised. Additionally, more Go linters have been enabled and
+improvements have been made to the devcontainer.
+
+* Changed `linkerd-init` resource (CPU/memory) limits and requests to ensure by
+  default the init container does not break a pod's `Guaranteed` QOS class
+* Added a new check condition to skip pods whose status is `NodeShutdown`
+  during validation as they will not have a proxy container
+* Fixed an issue that would prevent proxies from sending HTTP Upgrade requests
+  (used in websockets) through multi-cluster gateways
+
 ## edge-22.3.1
 
 This edge release includes updates to dependencies, CI, and rust 1.59.0. It also
diff --git a/Cargo.lock b/Cargo.lock
index ee7c7eae8..2c3f0b322 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -39,9 +39,9 @@ checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
 
 [[package]]
 name = "async-stream"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625"
+checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e"
 dependencies = [
  "async-stream-impl",
  "futures-core",
@@ -49,9 +49,9 @@ dependencies = [
 
 [[package]]
 name = "async-stream-impl"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308"
+checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -806,9 +806,9 @@ dependencies = [
 
 [[package]]
 name = "kubert"
-version = "0.3.3"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b587b7718e2c44d3c045fdd5c3a3515317200b14319439ad41089818078d16e2"
+checksum = "db6d5039ebf63d1978760db7e835579aa790511979cb78eb9d4dcdd45f1112c5"
 dependencies = [
  "clap",
  "drain",
@@ -1954,9 +1954,9 @@ dependencies = [
 
 [[package]]
 name = "tower-http"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90c125fdea84614a4368fd35786b51d0682ab8d42705e061e92f0b955dea40fb"
+checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8"
 dependencies = [
  "base64",
  "bitflags",
diff --git a/charts/linkerd-control-plane/Chart.yaml b/charts/linkerd-control-plane/Chart.yaml
index 40d758df9..32693f713 100644
--- a/charts/linkerd-control-plane/Chart.yaml
+++ b/charts/linkerd-control-plane/Chart.yaml
@@ -16,7 +16,7 @@ dependencies:
 - name: partials
   version: 0.1.0
   repository: file://../partials
-version: 1.1.8-edge
+version: 1.1.9-edge
 icon: https://linkerd.io/images/logo-only-200h.png
 maintainers:
   - name: Linkerd authors
diff --git a/charts/linkerd-control-plane/README.md b/charts/linkerd-control-plane/README.md
index 4c23ce0b0..1d4c674fa 100644
--- a/charts/linkerd-control-plane/README.md
+++ b/charts/linkerd-control-plane/README.md
@@ -3,7 +3,7 @@
 Linkerd gives you observability, reliability, and security
 for your microservices — with no code change required.
 
-![Version: 1.1.8-edge](https://img.shields.io/badge/Version-1.1.8--edge-informational?style=flat-square)
+![Version: 1.1.9-edge](https://img.shields.io/badge/Version-1.1.9--edge-informational?style=flat-square)
 ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square)
 ![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)
 
diff --git a/charts/linkerd2-cni/Chart.yaml b/charts/linkerd2-cni/Chart.yaml
index 944467db8..04913fb8b 100644
--- a/charts/linkerd2-cni/Chart.yaml
+++ b/charts/linkerd2-cni/Chart.yaml
@@ -9,5 +9,5 @@ description: |
 kubeVersion: ">=1.20.0-0"
 icon: https://linkerd.io/images/logo-only-200h.png
 name: "linkerd2-cni"
-version: 30.0.8-edge
+version: 30.0.9-edge
 
diff --git a/charts/linkerd2-cni/README.md b/charts/linkerd2-cni/README.md
index 6b5a7884c..a7262da4b 100644
--- a/charts/linkerd2-cni/README.md
+++ b/charts/linkerd2-cni/README.md
@@ -6,7 +6,7 @@ Linkerd [CNI plugin](https://linkerd.io/2/features/cni/) takes care of setting
 up your pod's network so  incoming and outgoing traffic is proxied through the
 data plane.
 
-![Version: 30.0.8-edge](https://img.shields.io/badge/Version-30.0.8--edge-informational?style=flat-square)
+![Version: 30.0.9-edge](https://img.shields.io/badge/Version-30.0.9--edge-informational?style=flat-square)
 
 ![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)
 
diff --git a/go.mod b/go.mod
index 1a220aa3d..323fce6c8 100644
--- a/go.mod
+++ b/go.mod
@@ -32,7 +32,7 @@ require (
 	github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
 	github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
 	github.com/sirupsen/logrus v1.8.1
-	github.com/spf13/cobra v1.3.0
+	github.com/spf13/cobra v1.4.0
 	github.com/spf13/pflag v1.0.5
 	go.opencensus.io v0.23.0
 	golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d
diff --git a/go.sum b/go.sum
index b5f003a1b..9dfbf19c5 100644
--- a/go.sum
+++ b/go.sum
@@ -876,8 +876,9 @@ github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
 github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
 github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
 github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
-github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
 github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
+github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
+github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
diff --git a/jaeger/charts/linkerd-jaeger/Chart.yaml b/jaeger/charts/linkerd-jaeger/Chart.yaml
index 474772c88..0dee25982 100644
--- a/jaeger/charts/linkerd-jaeger/Chart.yaml
+++ b/jaeger/charts/linkerd-jaeger/Chart.yaml
@@ -11,7 +11,7 @@ kubeVersion: ">=1.20.0-0"
 name: linkerd-jaeger
 sources:
 - https://github.com/linkerd/linkerd2/
-version: 30.2.4-edge
+version: 30.2.5-edge
 icon: https://linkerd.io/images/logo-only-200h.png
 maintainers:
   - name: Linkerd authors
diff --git a/jaeger/charts/linkerd-jaeger/README.md b/jaeger/charts/linkerd-jaeger/README.md
index acc143f37..452634ef9 100644
--- a/jaeger/charts/linkerd-jaeger/README.md
+++ b/jaeger/charts/linkerd-jaeger/README.md
@@ -3,7 +3,7 @@
 The Linkerd-Jaeger extension adds distributed tracing to Linkerd using
 OpenCensus and Jaeger.
 
-![Version: 30.2.4-edge](https://img.shields.io/badge/Version-30.2.4--edge-informational?style=flat-square)
+![Version: 30.2.5-edge](https://img.shields.io/badge/Version-30.2.5--edge-informational?style=flat-square)
 
 ![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)
 
diff --git a/multicluster/charts/linkerd-multicluster/Chart.yaml b/multicluster/charts/linkerd-multicluster/Chart.yaml
index fea6b139e..277b1c8c7 100644
--- a/multicluster/charts/linkerd-multicluster/Chart.yaml
+++ b/multicluster/charts/linkerd-multicluster/Chart.yaml
@@ -11,7 +11,7 @@ kubeVersion: ">=1.20.0-0"
 name: "linkerd-multicluster"
 sources:
 - https://github.com/linkerd/linkerd2/
-version: 30.0.7-edge
+version: 30.0.8-edge
 icon: https://linkerd.io/images/logo-only-200h.png
 maintainers:
   - name: Linkerd authors
diff --git a/multicluster/charts/linkerd-multicluster/README.md b/multicluster/charts/linkerd-multicluster/README.md
index fd758b0ad..2b97890c1 100644
--- a/multicluster/charts/linkerd-multicluster/README.md
+++ b/multicluster/charts/linkerd-multicluster/README.md
@@ -3,7 +3,7 @@
 The Linkerd-Multicluster extension contains resources to support multicluster
 linking to remote clusters
 
-![Version: 30.0.7-edge](https://img.shields.io/badge/Version-30.0.7--edge-informational?style=flat-square)
+![Version: 30.0.8-edge](https://img.shields.io/badge/Version-30.0.8--edge-informational?style=flat-square)
 
 ![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)
 
diff --git a/multicluster/cmd/service-mirror/main.go b/multicluster/cmd/service-mirror/main.go
index bae0dca1c..75ef86947 100644
--- a/multicluster/cmd/service-mirror/main.go
+++ b/multicluster/cmd/service-mirror/main.go
@@ -180,11 +180,19 @@ func restartClusterWatcher(
 		probeWorker.Stop()
 	}
 
+	// Start probe worker
+	workerMetrics, err := metrics.NewWorkerMetrics(link.TargetClusterName)
+	if err != nil {
+		return fmt.Errorf("failed to create metrics for cluster watcher: %w", err)
+	}
+	probeWorker = servicemirror.NewProbeWorker(fmt.Sprintf("probe-gateway-%s", link.TargetClusterName), &link.ProbeSpec, workerMetrics, link.TargetClusterName)
+	probeWorker.Start()
+
+	// Start cluster watcher
 	cfg, err := clientcmd.RESTConfigFromKubeConfig(creds)
 	if err != nil {
 		return fmt.Errorf("unable to parse kube config: %w", err)
 	}
-
 	clusterWatcher, err = servicemirror.NewRemoteClusterServiceWatcher(
 		ctx,
 		namespace,
@@ -193,22 +201,16 @@ func restartClusterWatcher(
 		&link,
 		requeueLimit,
 		repairPeriod,
+		probeWorker.Liveness,
 		enableHeadlessSvc,
 	)
 	if err != nil {
 		return fmt.Errorf("unable to create cluster watcher: %w", err)
 	}
-
 	err = clusterWatcher.Start(ctx)
 	if err != nil {
 		return fmt.Errorf("failed to start cluster watcher: %w", err)
 	}
 
-	workerMetrics, err := metrics.NewWorkerMetrics(link.TargetClusterName)
-	if err != nil {
-		return fmt.Errorf("failed to create metrics for cluster watcher: %w", err)
-	}
-	probeWorker = servicemirror.NewProbeWorker(fmt.Sprintf("probe-gateway-%s", link.TargetClusterName), &link.ProbeSpec, workerMetrics, link.TargetClusterName)
-	probeWorker.Start()
 	return nil
 }
diff --git a/multicluster/service-mirror/cluster_watcher.go b/multicluster/service-mirror/cluster_watcher.go
index 40c038401..bc13e3f54 100644
--- a/multicluster/service-mirror/cluster_watcher.go
+++ b/multicluster/service-mirror/cluster_watcher.go
@@ -50,6 +50,8 @@ type (
 		eventsQueue             workqueue.RateLimitingInterface
 		requeueLimit            int
 		repairPeriod            time.Duration
+		gatewayAlive            bool
+		liveness                chan bool
 		headlessServicesEnabled bool
 	}
 
@@ -156,6 +158,7 @@ func NewRemoteClusterServiceWatcher(
 	link *multicluster.Link,
 	requeueLimit int,
 	repairPeriod time.Duration,
+	liveness chan bool,
 	enableHeadlessSvc bool,
 ) (*RemoteClusterServiceWatcher, error) {
 	remoteAPI, err := k8s.InitializeAPIForConfig(ctx, cfg, false, k8s.Svc, k8s.Endpoint)
@@ -191,6 +194,7 @@ func NewRemoteClusterServiceWatcher(
 		eventsQueue:             workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
 		requeueLimit:            requeueLimit,
 		repairPeriod:            repairPeriod,
+		liveness:                liveness,
 		headlessServicesEnabled: enableHeadlessSvc,
 	}, nil
 }
@@ -434,7 +438,8 @@ func (rcsw *RemoteClusterServiceWatcher) handleRemoteServiceUpdated(ctx context.
 	}
 	copiedEndpoints.Annotations[consts.RemoteGatewayIdentity] = rcsw.link.GatewayIdentity
 
-	if _, err := rcsw.localAPIClient.Client.CoreV1().Endpoints(copiedEndpoints.Namespace).Update(ctx, copiedEndpoints, metav1.UpdateOptions{}); err != nil {
+	err = rcsw.createOrUpdateEndpoints(ctx, copiedEndpoints)
+	if err != nil {
 		return RetryableError{[]error{err}}
 	}
 
@@ -594,8 +599,16 @@ func (rcsw *RemoteClusterServiceWatcher) createGatewayEndpoints(ctx context.Cont
 				Ports:     rcsw.getEndpointsPorts(exportedService),
 			},
 		}
+	} else if !empty {
+		endpointsToCreate.Subsets = []corev1.EndpointSubset{
+			{
+				NotReadyAddresses: gatewayAddresses,
+				Ports:             rcsw.getEndpointsPorts(exportedService),
+			},
+		}
+		rcsw.log.Warnf("could not resolve gateway addresses for %s; setting endpoint subsets to not ready", serviceInfo)
 	} else {
-		rcsw.log.Warnf("exported service is empty or gateway for %s does not have ready addresses, skipping subsets", serviceInfo)
+		rcsw.log.Warnf("exported service %s is empty", serviceInfo)
 	}
 
 	if rcsw.link.GatewayIdentity != "" {
@@ -603,11 +616,11 @@ func (rcsw *RemoteClusterServiceWatcher) createGatewayEndpoints(ctx context.Cont
 	}
 
 	rcsw.log.Infof("Creating a new endpoints for %s", serviceInfo)
-	if _, err := rcsw.localAPIClient.Client.CoreV1().Endpoints(exportedService.Namespace).Create(ctx, endpointsToCreate, metav1.CreateOptions{}); err != nil {
+	err = rcsw.createOrUpdateEndpoints(ctx, endpointsToCreate)
+	if err != nil {
 		if svcErr := rcsw.localAPIClient.Client.CoreV1().Services(exportedService.Namespace).Delete(ctx, localServiceName, metav1.DeleteOptions{}); svcErr != nil {
-			rcsw.log.Errorf("failed to delete Service %s after Endpoints creation failed: %s", localServiceName, svcErr)
+			rcsw.log.Errorf("Failed to delete service %s after endpoints creation failed: %s", localServiceName, svcErr)
 		}
-		// and retry
 		return RetryableError{[]error{err}}
 	}
 	return nil
@@ -672,13 +685,12 @@ func (rcsw *RemoteClusterServiceWatcher) createOrUpdateService(service *corev1.S
 	return nil
 }
 
-func (rcsw *RemoteClusterServiceWatcher) getMirrorServices() ([]*corev1.Service, error) {
+func (rcsw *RemoteClusterServiceWatcher) getMirrorServices() (*corev1.ServiceList, error) {
 	matchLabels := map[string]string{
 		consts.MirroredResourceLabel:  "true",
 		consts.RemoteClusterNameLabel: rcsw.link.TargetClusterName,
 	}
-
-	services, err := rcsw.localAPIClient.Svc().Lister().List(labels.Set(matchLabels).AsSelector())
+	services, err := rcsw.localAPIClient.Client.CoreV1().Services("").List(context.Background(), metav1.ListOptions{LabelSelector: labels.SelectorFromSet(matchLabels).String()})
 	if err != nil {
 		return nil, err
 	}
@@ -860,6 +872,11 @@ func (rcsw *RemoteClusterServiceWatcher) Start(ctx context.Context) error {
 			case <-ticker.C:
 				ev := RepairEndpoints{}
 				rcsw.eventsQueue.Add(&ev)
+			case alive := <-rcsw.liveness:
+				rcsw.log.Debugf("gateway liveness change from %t to %t", rcsw.gatewayAlive, alive)
+				rcsw.gatewayAlive = alive
+				ev := RepairEndpoints{}
+				rcsw.eventsQueue.Add(&ev)
 			case <-rcsw.stopper:
 				return
 			}
@@ -901,44 +918,17 @@ func (rcsw *RemoteClusterServiceWatcher) resolveGatewayAddress() ([]corev1.Endpo
 }
 
 func (rcsw *RemoteClusterServiceWatcher) repairEndpoints(ctx context.Context) error {
-	gatewayAddresses, err := rcsw.resolveGatewayAddress()
-	if err != nil {
-		return err
-	}
-
 	endpointRepairCounter.With(prometheus.Labels{
 		gatewayClusterName: rcsw.link.TargetClusterName,
 	}).Inc()
 
-	// Create or update gateway mirror endpoints.
-	gatewayMirrorName := fmt.Sprintf("probe-gateway-%s", rcsw.link.TargetClusterName)
-
-	gatewayMirrorEndpoints := &corev1.Endpoints{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      gatewayMirrorName,
-			Namespace: rcsw.serviceMirrorNamespace,
-			Labels: map[string]string{
-				consts.RemoteClusterNameLabel: rcsw.link.TargetClusterName,
-			},
-			Annotations: map[string]string{
-				consts.RemoteGatewayIdentity: rcsw.link.GatewayIdentity,
-			},
-		},
-		Subsets: []corev1.EndpointSubset{
-			{
-				Addresses: gatewayAddresses,
-				Ports: []corev1.EndpointPort{
-					{
-						Name:     "mc-probe",
-						Port:     int32(rcsw.link.ProbeSpec.Port),
-						Protocol: "TCP",
-					},
-				},
-			},
-		},
+	// Create or update the gateway mirror endpoints responsible for driving
+	// the cluster watcher's gateway liveness status.
+	gatewayAddresses, err := rcsw.resolveGatewayAddress()
+	if err != nil {
+		return err
 	}
-
-	err = rcsw.createOrUpdateEndpoints(ctx, gatewayMirrorEndpoints)
+	err = rcsw.createOrUpdateGatewayEndpoints(ctx, gatewayAddresses)
 	if err != nil {
 		rcsw.log.Errorf("Failed to create/update gateway mirror endpoints: %s", err)
 	}
@@ -948,8 +938,8 @@ func (rcsw *RemoteClusterServiceWatcher) repairEndpoints(ctx context.Context) er
 	if err != nil {
 		rcsw.log.Errorf("Failed to list mirror services: %s", err)
 	}
-	for _, svc := range mirrorServices {
-		updatedService := svc.DeepCopy()
+	for _, svc := range mirrorServices.Items {
+		updatedService := svc
 
 		// Mirrors for headless services are also headless, and their
 		// Endpoints point to auxiliary services instead of pointing to
@@ -968,15 +958,21 @@ func (rcsw *RemoteClusterServiceWatcher) repairEndpoints(ctx context.Context) er
 
 		endpoints, err := rcsw.localAPIClient.Endpoint().Lister().Endpoints(svc.Namespace).Get(svc.Name)
 		if err != nil {
-			rcsw.log.Errorf("Could not get local endpoints: %s", err)
-			continue
+			if !kerrors.IsNotFound(err) {
+				rcsw.log.Errorf("failed to list local endpoints: %s", err)
+				continue
+			}
+			endpoints, err = rcsw.localAPIClient.Client.CoreV1().Endpoints(svc.Namespace).Get(ctx, svc.Name, metav1.GetOptions{})
+			if err != nil {
+				rcsw.log.Errorf("failed to get local endpoints %s/%s: %s", svc.Namespace, svc.Name, err)
+			}
 		}
 
 		updatedEndpoints := endpoints.DeepCopy()
 		updatedEndpoints.Subsets = []corev1.EndpointSubset{
 			{
 				Addresses: gatewayAddresses,
-				Ports:     rcsw.getEndpointsPorts(updatedService),
+				Ports:     rcsw.getEndpointsPorts(&updatedService),
 			},
 		}
 
@@ -989,6 +985,7 @@ func (rcsw *RemoteClusterServiceWatcher) repairEndpoints(ctx context.Context) er
 			continue
 		}
 		if empty {
+			rcsw.log.Warnf("exported service %s/%s is empty", targetService.Namespace, targetService.Name)
 			updatedEndpoints.Subsets = []corev1.EndpointSubset{}
 		}
 
@@ -997,13 +994,12 @@ func (rcsw *RemoteClusterServiceWatcher) repairEndpoints(ctx context.Context) er
 		}
 		updatedEndpoints.Annotations[consts.RemoteGatewayIdentity] = rcsw.link.GatewayIdentity
 
-		_, err = rcsw.localAPIClient.Client.CoreV1().Services(updatedService.Namespace).Update(ctx, updatedService, metav1.UpdateOptions{})
+		_, err = rcsw.localAPIClient.Client.CoreV1().Services(updatedService.Namespace).Update(ctx, &updatedService, metav1.UpdateOptions{})
 		if err != nil {
 			rcsw.log.Error(err)
 			continue
 		}
-
-		_, err = rcsw.localAPIClient.Client.CoreV1().Endpoints(updatedService.Namespace).Update(ctx, updatedEndpoints, metav1.UpdateOptions{})
+		err = rcsw.createOrUpdateEndpoints(ctx, updatedEndpoints)
 		if err != nil {
 			rcsw.log.Error(err)
 		}
@@ -1012,22 +1008,57 @@ func (rcsw *RemoteClusterServiceWatcher) repairEndpoints(ctx context.Context) er
 	return nil
 }
 
-func (rcsw *RemoteClusterServiceWatcher) createOrUpdateEndpoints(ctx context.Context, ep *corev1.Endpoints) error {
-	_, err := rcsw.localAPIClient.Client.CoreV1().Endpoints(ep.Namespace).Get(ctx, ep.Name, metav1.GetOptions{})
+// createOrUpdateGatewayEndpoints will create or update the gateway mirror
+// endpoints for a remote cluster. These endpoints are required for the probe
+// worker responsible for probing gateway liveness, so these endpoints are
+// never in a not ready state.
+func (rcsw *RemoteClusterServiceWatcher) createOrUpdateGatewayEndpoints(ctx context.Context, addressses []v1.EndpointAddress) error {
+	gatewayMirrorName := fmt.Sprintf("probe-gateway-%s", rcsw.link.TargetClusterName)
+	endpoints := &corev1.Endpoints{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      gatewayMirrorName,
+			Namespace: rcsw.serviceMirrorNamespace,
+			Labels: map[string]string{
+				consts.RemoteClusterNameLabel: rcsw.link.TargetClusterName,
+			},
+			Annotations: map[string]string{
+				consts.RemoteGatewayIdentity: rcsw.link.GatewayIdentity,
+			},
+		},
+		Subsets: []corev1.EndpointSubset{
+			{
+				Addresses: addressses,
+				Ports: []corev1.EndpointPort{
+					{
+						Name:     "mc-probe",
+						Port:     int32(rcsw.link.ProbeSpec.Port),
+						Protocol: "TCP",
+					},
+				},
+			},
+		},
+	}
+	_, err := rcsw.localAPIClient.Client.CoreV1().Endpoints(endpoints.Namespace).Get(ctx, endpoints.Name, metav1.GetOptions{})
 	if err != nil {
 		if !kerrors.IsNotFound(err) {
 			return err
 		}
 
-		// Does not exist so we should create it.
-		_, err = rcsw.localAPIClient.Client.CoreV1().Endpoints(ep.Namespace).Create(ctx, ep, metav1.CreateOptions{})
+		// Mirror endpoints for the gateway do not exist so they need to be
+		// created. As mentioned above, these endpoints are required for the
+		// probe worker and therefore should never be put in a not ready
+		// state.
+		_, err = rcsw.localAPIClient.Client.CoreV1().Endpoints(endpoints.Namespace).Create(ctx, endpoints, metav1.CreateOptions{})
 		if err != nil {
 			return err
 		}
+		return nil
 	}
 
-	// Exists so we should update it.
-	_, err = rcsw.localAPIClient.Client.CoreV1().Endpoints(ep.Namespace).Update(ctx, ep, metav1.UpdateOptions{})
+	// Mirror endpoints for the gateway already exist so they need to be
+	// updated. As mentioned above, these endpoints are required for the probe
+	// worker and therefore should never be put in a not ready state.
+	_, err = rcsw.localAPIClient.Client.CoreV1().Endpoints(endpoints.Namespace).Update(ctx, endpoints, metav1.UpdateOptions{})
 	return err
 }
 
@@ -1079,86 +1110,34 @@ func (rcsw *RemoteClusterServiceWatcher) handleCreateOrUpdateEndpoints(
 			},
 		}
 	}
-
-	_, err = rcsw.localAPIClient.Client.CoreV1().Endpoints(exportedEndpoints.Namespace).Update(ctx, ep, metav1.UpdateOptions{})
-	return err
+	return rcsw.createOrUpdateEndpoints(ctx, ep)
 }
 
-// createEndpointMirrorService creates a new Endpoint Mirror service and its
-// corresponding endpoints object. It returns the newly created Endpoint Mirror
-// service object. When a headless service is exported, we create a Headless
-// Mirror service in the source cluster and then for each hostname in the
-// exported service's endpoints object, we also create an Endpoint Mirror
-// service (and its corresponding endpoints object).
-func (rcsw *RemoteClusterServiceWatcher) createEndpointMirrorService(ctx context.Context, endpointHostname, resourceVersion, endpointMirrorName string, exportedService *corev1.Service) (*corev1.Service, error) {
-	gatewayAddresses, err := rcsw.resolveGatewayAddress()
-	if err != nil {
-		return nil, err
-	}
-
-	endpointMirrorAnnotations := map[string]string{
-		consts.RemoteResourceVersionAnnotation: resourceVersion, // needed to detect real changes
-		consts.RemoteServiceFqName:             fmt.Sprintf("%s.%s.%s.svc.%s", endpointHostname, exportedService.Name, exportedService.Namespace, rcsw.link.TargetClusterDomain),
-	}
-
-	endpointMirrorLabels := rcsw.getMirroredServiceLabels(nil)
-	mirrorServiceName := rcsw.mirroredResourceName(exportedService.Name)
-	endpointMirrorLabels[consts.MirroredHeadlessSvcNameLabel] = mirrorServiceName
-
-	// Create service spec, clusterIP
-	endpointMirrorService := &corev1.Service{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:        endpointMirrorName,
-			Namespace:   exportedService.Namespace,
-			Annotations: endpointMirrorAnnotations,
-			Labels:      endpointMirrorLabels,
-		},
-		Spec: corev1.ServiceSpec{
-			Ports: remapRemoteServicePorts(exportedService.Spec.Ports),
-		},
-	}
-	endpointMirrorEndpoints := &corev1.Endpoints{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      endpointMirrorService.Name,
-			Namespace: endpointMirrorService.Namespace,
-			Labels:    endpointMirrorLabels,
-			Annotations: map[string]string{
-				consts.RemoteServiceFqName: endpointMirrorService.Annotations[consts.RemoteServiceFqName],
-			},
-		},
-		Subsets: []corev1.EndpointSubset{
-			{
-				Addresses: gatewayAddresses,
-				Ports:     rcsw.getEndpointsPorts(exportedService),
-			},
-		},
-	}
-
-	if rcsw.link.GatewayIdentity != "" {
-		endpointMirrorEndpoints.Annotations[consts.RemoteGatewayIdentity] = rcsw.link.GatewayIdentity
+// createOrUpdateEndpoints will create or update endpoints based off gateway
+// liveness. If the gateway is not alive, then the addresses in each subset
+// will be set to not ready; these will moved back to ready once the gateway
+// is alive and an update is received.
+func (rcsw *RemoteClusterServiceWatcher) createOrUpdateEndpoints(ctx context.Context, endpoints *corev1.Endpoints) error {
+	if !rcsw.gatewayAlive {
+		rcsw.log.Warnf("gateway for %s/%s does not have ready addresses; setting addresses to not ready", endpoints.Namespace, endpoints.Name)
+		for i := range endpoints.Subsets {
+			endpoints.Subsets[i].NotReadyAddresses = append(endpoints.Subsets[i].NotReadyAddresses, endpoints.Subsets[i].Addresses...)
+			endpoints.Subsets[i].Addresses = nil
+		}
 	}
-
-	exportedServiceInfo := fmt.Sprintf("%s/%s", exportedService.Namespace, exportedService.Name)
-	endpointMirrorInfo := fmt.Sprintf("%s/%s", endpointMirrorService.Namespace, endpointMirrorName)
-	rcsw.log.Infof("Creating a new endpoint mirror service %s for exported headless service %s", endpointMirrorInfo, exportedServiceInfo)
-	createdService, err := rcsw.localAPIClient.Client.CoreV1().Services(endpointMirrorService.Namespace).Create(ctx, endpointMirrorService, metav1.CreateOptions{})
+	_, err := rcsw.localAPIClient.Client.CoreV1().Endpoints(endpoints.Namespace).Get(ctx, endpoints.Name, metav1.GetOptions{})
 	if err != nil {
-		if !kerrors.IsAlreadyExists(err) {
-			// we might have created it during earlier attempt, if that is not the case, we retry
-			return createdService, RetryableError{[]error{err}}
+		if !kerrors.IsNotFound(err) {
+			return fmt.Errorf("Failed to get endpoints for %s/%s: %w", endpoints.Namespace, endpoints.Name, err)
 		}
-	}
-
-	rcsw.log.Infof("Creating a new endpoints object for endpoint mirror service %s", endpointMirrorInfo)
-	if _, err := rcsw.localAPIClient.Client.CoreV1().Endpoints(endpointMirrorService.Namespace).Create(ctx, endpointMirrorEndpoints, metav1.CreateOptions{}); err != nil {
-		if svcErr := rcsw.localAPIClient.Client.CoreV1().Services(endpointMirrorService.Namespace).Delete(ctx, endpointMirrorName, metav1.DeleteOptions{}); svcErr != nil {
-			rcsw.log.Errorf("failed to delete Service %s after Endpoints creation failed: %s", endpointMirrorName, svcErr)
+		_, err := rcsw.localAPIClient.Client.CoreV1().Endpoints(endpoints.Namespace).Create(ctx, endpoints, metav1.CreateOptions{})
+		if err != nil {
+			return fmt.Errorf("Failed to create endpoints for %s/%s: %w", endpoints.Namespace, endpoints.Name, err)
 		}
-		// and retry
-		return createdService, RetryableError{[]error{err}}
+		return nil
 	}
-
-	return createdService, nil
+	_, err = rcsw.localAPIClient.Client.CoreV1().Endpoints(endpoints.Namespace).Update(ctx, endpoints, metav1.UpdateOptions{})
+	return err
 }
 
 func isExportedEndpoints(obj interface{}, log *logging.Entry) bool {
diff --git a/multicluster/service-mirror/cluster_watcher_headless.go b/multicluster/service-mirror/cluster_watcher_headless.go
index 9aae86bef..5b44e1045 100644
--- a/multicluster/service-mirror/cluster_watcher_headless.go
+++ b/multicluster/service-mirror/cluster_watcher_headless.go
@@ -167,9 +167,9 @@ func (rcsw *RemoteClusterServiceWatcher) createOrUpdateHeadlessEndpoints(ctx con
 		return RetryableError{errors}
 	}
 
-	// Update
+	// Update endpoints
 	mirrorEndpoints.Subsets = newSubsets
-	_, err = rcsw.localAPIClient.Client.CoreV1().Endpoints(mirrorEndpoints.Namespace).Update(ctx, mirrorEndpoints, metav1.UpdateOptions{})
+	err = rcsw.createOrUpdateEndpoints(ctx, mirrorEndpoints)
 	if err != nil {
 		return RetryableError{[]error{err}}
 	}
@@ -302,9 +302,6 @@ func (rcsw *RemoteClusterServiceWatcher) createHeadlessMirrorEndpoints(ctx conte
 
 	rcsw.log.Infof("Creating a new headless mirror endpoints object for headless mirror %s/%s", headlessMirrorServiceName, exportedService.Namespace)
 	if _, err := rcsw.localAPIClient.Client.CoreV1().Endpoints(exportedService.Namespace).Create(ctx, headlessMirrorEndpoints, metav1.CreateOptions{}); err != nil {
-		// This is a precautionary deletion because creation failed, so we
-		// ignore the error if Delete also fails.
-		//nolint:errcheck
 		if svcErr := rcsw.localAPIClient.Client.CoreV1().Services(exportedService.Namespace).Delete(ctx, headlessMirrorServiceName, metav1.DeleteOptions{}); svcErr != nil {
 			rcsw.log.Errorf("failed to delete Service %s after Endpoints creation failed: %s", headlessMirrorServiceName, svcErr)
 		}
@@ -315,6 +312,89 @@ func (rcsw *RemoteClusterServiceWatcher) createHeadlessMirrorEndpoints(ctx conte
 	return nil
 }
 
+// createEndpointMirrorService creates a new Endpoint Mirror service and its
+// corresponding endpoints object. It returns the newly created Endpoint Mirror
+// service object. When a headless service is exported, we create a Headless
+// Mirror service in the source cluster and then for each hostname in the
+// exported service's endpoints object, we also create an Endpoint Mirror
+// service (and its corresponding endpoints object).
+func (rcsw *RemoteClusterServiceWatcher) createEndpointMirrorService(ctx context.Context, endpointHostname, resourceVersion, endpointMirrorName string, exportedService *corev1.Service) (*corev1.Service, error) {
+	gatewayAddresses, err := rcsw.resolveGatewayAddress()
+	if err != nil {
+		return nil, err
+	}
+
+	endpointMirrorAnnotations := map[string]string{
+		consts.RemoteResourceVersionAnnotation: resourceVersion, // needed to detect real changes
+		consts.RemoteServiceFqName:             fmt.Sprintf("%s.%s.%s.svc.%s", endpointHostname, exportedService.Name, exportedService.Namespace, rcsw.link.TargetClusterDomain),
+	}
+
+	endpointMirrorLabels := rcsw.getMirroredServiceLabels(nil)
+	mirrorServiceName := rcsw.mirroredResourceName(exportedService.Name)
+	endpointMirrorLabels[consts.MirroredHeadlessSvcNameLabel] = mirrorServiceName
+
+	// Create service spec, clusterIP
+	endpointMirrorService := &corev1.Service{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:        endpointMirrorName,
+			Namespace:   exportedService.Namespace,
+			Annotations: endpointMirrorAnnotations,
+			Labels:      endpointMirrorLabels,
+		},
+		Spec: corev1.ServiceSpec{
+			Ports: remapRemoteServicePorts(exportedService.Spec.Ports),
+		},
+	}
+	endpointMirrorEndpoints := &corev1.Endpoints{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      endpointMirrorService.Name,
+			Namespace: endpointMirrorService.Namespace,
+			Labels:    endpointMirrorLabels,
+			Annotations: map[string]string{
+				consts.RemoteServiceFqName: endpointMirrorService.Annotations[consts.RemoteServiceFqName],
+			},
+		},
+		Subsets: []corev1.EndpointSubset{
+			{
+				Addresses: gatewayAddresses,
+				Ports:     rcsw.getEndpointsPorts(exportedService),
+			},
+		},
+	}
+
+	if rcsw.link.GatewayIdentity != "" {
+		endpointMirrorEndpoints.Annotations[consts.RemoteGatewayIdentity] = rcsw.link.GatewayIdentity
+	}
+
+	exportedServiceInfo := fmt.Sprintf("%s/%s", exportedService.Namespace, exportedService.Name)
+	endpointMirrorInfo := fmt.Sprintf("%s/%s", endpointMirrorService.Namespace, endpointMirrorName)
+	rcsw.log.Infof("Creating a new endpoint mirror service %s for exported headless service %s", endpointMirrorInfo, exportedServiceInfo)
+	createdService, err := rcsw.localAPIClient.Client.CoreV1().Services(endpointMirrorService.Namespace).Create(ctx, endpointMirrorService, metav1.CreateOptions{})
+	if err != nil {
+		if !kerrors.IsAlreadyExists(err) {
+			// we might have created it during earlier attempt, if that is not the case, we retry
+			return createdService, RetryableError{[]error{err}}
+		}
+	}
+
+	// todo: The endpoints are created without checking for gateway liveness.
+	// We do this because if we do check — and the gateway is down — these endpoints are
+	// never repaired. This is because in repairEndpoints we skip local
+	// endpoints that are mirroring headless services since they may not have
+	// a matching endpoint on the remote cluster.
+	//
+	// Explained by cluster_watcher.go L943-L945.
+	rcsw.log.Infof("Creating a new endpoints object for endpoint mirror service %s", endpointMirrorInfo)
+	if _, err := rcsw.localAPIClient.Client.CoreV1().Endpoints(endpointMirrorService.Namespace).Create(ctx, endpointMirrorEndpoints, metav1.CreateOptions{}); err != nil {
+		if svcErr := rcsw.localAPIClient.Client.CoreV1().Services(endpointMirrorService.Namespace).Delete(ctx, endpointMirrorName, metav1.DeleteOptions{}); svcErr != nil {
+			rcsw.log.Errorf("Failed to delete service %s after endpoints creation failed: %s", endpointMirrorName, svcErr)
+		}
+		return createdService, RetryableError{[]error{err}}
+	}
+
+	return createdService, nil
+}
+
 // shouldExportAsHeadlessService checks if an exported service should be
 // mirrored as a headless service or as a clusterIP service, based on its
 // endpoints object. For an exported service to be a headless mirror, it needs
diff --git a/multicluster/service-mirror/probe_worker.go b/multicluster/service-mirror/probe_worker.go
index cf983029c..a1d0104e2 100644
--- a/multicluster/service-mirror/probe_worker.go
+++ b/multicluster/service-mirror/probe_worker.go
@@ -16,6 +16,8 @@ const httpGatewayTimeoutMillis = 50000
 // ProbeWorker is responsible for monitoring gateways using a probe specification
 type ProbeWorker struct {
 	localGatewayName string
+	alive            bool
+	Liveness         chan bool
 	*sync.RWMutex
 	probeSpec *multicluster.ProbeSpec
 	stopCh    chan struct{}
@@ -27,6 +29,7 @@ type ProbeWorker struct {
 func NewProbeWorker(localGatewayName string, spec *multicluster.ProbeSpec, metrics *ProbeMetrics, probekey string) *ProbeWorker {
 	return &ProbeWorker{
 		localGatewayName: localGatewayName,
+		Liveness:         make(chan bool, 10),
 		RWMutex:          &sync.RWMutex{},
 		probeSpec:        spec,
 		stopCh:           make(chan struct{}),
@@ -97,17 +100,29 @@ func (pw *ProbeWorker) doProbe() {
 		pw.log.Warnf("Problem connecting with gateway. Marking as unhealthy %s", err)
 		pw.metrics.alive.Set(0)
 		pw.metrics.probes.With(notSuccessLabel).Inc()
+		if pw.alive {
+			pw.alive = false
+			pw.Liveness <- false
+		}
 		return
 	}
 	if resp.StatusCode != 200 {
 		pw.log.Warnf("Gateway returned unexpected status %d. Marking as unhealthy", resp.StatusCode)
 		pw.metrics.alive.Set(0)
 		pw.metrics.probes.With(notSuccessLabel).Inc()
+		if pw.alive {
+			pw.alive = false
+			pw.Liveness <- false
+		}
 	} else {
 		pw.log.Debug("Gateway is healthy")
 		pw.metrics.alive.Set(1)
 		pw.metrics.latencies.Observe(float64(end.Milliseconds()))
 		pw.metrics.probes.With(successLabel).Inc()
+		if !pw.alive {
+			pw.alive = true
+			pw.Liveness <- true
+		}
 	}
 
 	if err := resp.Body.Close(); err != nil {
diff --git a/policy-controller/Cargo.toml b/policy-controller/Cargo.toml
index a5cf2eb68..a5d380ded 100644
--- a/policy-controller/Cargo.toml
+++ b/policy-controller/Cargo.toml
@@ -35,7 +35,7 @@ features = [
 ]
 
 [dependencies.kubert]
-version = "0.3.3"
+version = "0.3.5"
 default-features = false
 features = [
     "clap",
diff --git a/policy-controller/k8s/index/Cargo.toml b/policy-controller/k8s/index/Cargo.toml
index 2152e1a70..dc901e8de 100644
--- a/policy-controller/k8s/index/Cargo.toml
+++ b/policy-controller/k8s/index/Cargo.toml
@@ -11,7 +11,7 @@ anyhow = "1"
 async-stream = "0.3"
 async-trait = "0.1"
 futures = { version = "0.3", default-features = false }
-kubert = { version = "0.3.3", default-features = false, features = ["initialized"] }
+kubert = { version = "0.3.5", default-features = false, features = ["initialized"] }
 linkerd-policy-controller-core = { path = "../../core" }
 linkerd-policy-controller-k8s-api = { path = "../api" }
 parking_lot = "0.12"
diff --git a/viz/charts/linkerd-viz/Chart.yaml b/viz/charts/linkerd-viz/Chart.yaml
index e10fb59d5..32717ae58 100644
--- a/viz/charts/linkerd-viz/Chart.yaml
+++ b/viz/charts/linkerd-viz/Chart.yaml
@@ -11,7 +11,7 @@ kubeVersion: ">=1.20.0-0"
 name: "linkerd-viz"
 sources:
 - https://github.com/linkerd/linkerd2/
-version: 30.0.7-edge
+version: 30.0.8-edge
 icon: https://linkerd.io/images/logo-only-200h.png
 maintainers:
   - name: Linkerd authors
diff --git a/viz/charts/linkerd-viz/README.md b/viz/charts/linkerd-viz/README.md
index 04c7f8976..7919853ed 100644
--- a/viz/charts/linkerd-viz/README.md
+++ b/viz/charts/linkerd-viz/README.md
@@ -3,7 +3,7 @@
 The Linkerd-Viz extension contains observability and visualization
 components for Linkerd.
 
-![Version: 30.0.7-edge](https://img.shields.io/badge/Version-30.0.7--edge-informational?style=flat-square)
+![Version: 30.0.8-edge](https://img.shields.io/badge/Version-30.0.8--edge-informational?style=flat-square)
 
 ![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)
 
diff --git a/viz/tap/api/handlers.go b/viz/tap/api/handlers.go
index 562951ed5..d740dca27 100644
--- a/viz/tap/api/handlers.go
+++ b/viz/tap/api/handlers.go
@@ -157,7 +157,7 @@ func (h *handler) handleTap(w http.ResponseWriter, req *http.Request, p httprout
 
 	url := pkg.TapReqToURL(&tapReq)
 	if url != req.URL.Path {
-		err = fmt.Errorf("tap request body did not match APIServer URL: %+v != %q", url, req.URL.Path)
+		err = fmt.Errorf("tap request body did not match APIServer URL: %q != %q", url, req.URL.Path)
 		h.log.Error(err)
 		protohttp.WriteErrorToHTTPResponse(w, err)
 		return
@@ -175,7 +175,7 @@ func (h *handler) handleTap(w http.ResponseWriter, req *http.Request, p httprout
 	//nolint:staticcheck
 	err = h.grpcTapServer.TapByResource(&tapReq, &serverStream)
 	if err != nil {
-		h.log.Error(err)
+		h.log.Errorf("TapByResource failed: %q", err)
 		protohttp.WriteErrorToHTTPResponse(flushableWriter, err)
 		return
 	}
diff --git a/web/app/package.json b/web/app/package.json
index 63af95c02..8dd1fa282 100644
--- a/web/app/package.json
+++ b/web/app/package.json
@@ -56,10 +56,10 @@
     "babel-plugin-transform-react-remove-prop-types": "0.4.24",
     "chai": "4.3.6",
     "clean-webpack-plugin": "4.0.0",
-    "css-loader": "^6.7.0",
+    "css-loader": "^6.7.1",
     "enzyme": "3.11.0",
     "enzyme-adapter-react-16": "^1.15.6",
-    "eslint": "^8.10.0",
+    "eslint": "^8.11.0",
     "eslint-config-airbnb": "^19.0.4",
     "eslint-plugin-import": "^2.25.4",
     "eslint-plugin-jsx-a11y": "^6.5.1",
diff --git a/web/app/yarn.lock b/web/app/yarn.lock
index 78f20e591..b6977578a 100644
--- a/web/app/yarn.lock
+++ b/web/app/yarn.lock
@@ -1051,16 +1051,16 @@
     ts-node "^9"
     tslib "^2"
 
-"@eslint/eslintrc@^1.2.0":
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.0.tgz#7ce1547a5c46dfe56e1e45c3c9ed18038c721c6a"
-  integrity sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==
+"@eslint/eslintrc@^1.2.1":
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6"
+  integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==
   dependencies:
     ajv "^6.12.4"
     debug "^4.3.2"
     espree "^9.3.1"
     globals "^13.9.0"
-    ignore "^4.0.6"
+    ignore "^5.2.0"
     import-fresh "^3.2.1"
     js-yaml "^4.1.0"
     minimatch "^3.0.4"
@@ -2990,10 +2990,10 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
     shebang-command "^2.0.0"
     which "^2.0.1"
 
-css-loader@^6.7.0:
-  version "6.7.0"
-  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.0.tgz#c1200da1dfffe6643b18bda20fdd84cad3e36d39"
-  integrity sha512-S7HCfCiDHLA+VXKqdZwyRZgoO0R9BnKDnVIoHMq5grl3N86zAu7MB+FBWHr5xOJC8SmvpTLha/2NpfFkFEN/ig==
+css-loader@^6.7.1:
+  version "6.7.1"
+  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e"
+  integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==
   dependencies:
     icss-utils "^5.1.0"
     postcss "^8.4.7"
@@ -3775,12 +3775,12 @@ eslint-webpack-plugin@^3.1.1:
     normalize-path "^3.0.0"
     schema-utils "^3.1.1"
 
-eslint@^8.10.0:
-  version "8.10.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.10.0.tgz#931be395eb60f900c01658b278e05b6dae47199d"
-  integrity sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==
+eslint@^8.11.0:
+  version "8.11.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.11.0.tgz#88b91cfba1356fc10bb9eb592958457dfe09fb37"
+  integrity sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==
   dependencies:
-    "@eslint/eslintrc" "^1.2.0"
+    "@eslint/eslintrc" "^1.2.1"
     "@humanwhocodes/config-array" "^0.9.2"
     ajv "^6.10.0"
     chalk "^4.0.0"
@@ -4536,11 +4536,6 @@ ieee754@^1.1.13:
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
-ignore@^4.0.6:
-  version "4.0.6"
-  resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
-  integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
-
 ignore@^5.1.4, ignore@^5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"


Test patch:
diff --git a/cli/cmd/testdata/install_controlplane_tracing_output.golden b/cli/cmd/testdata/install_controlplane_tracing_output.golden
index 5fcde84b3..f6be85d9f 100644
--- a/cli/cmd/testdata/install_controlplane_tracing_output.golden
+++ b/cli/cmd/testdata/install_controlplane_tracing_output.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_custom_domain.golden b/cli/cmd/testdata/install_custom_domain.golden
index 5250b83c9..f4d2458b1 100644
--- a/cli/cmd/testdata/install_custom_domain.golden
+++ b/cli/cmd/testdata/install_custom_domain.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_custom_registry.golden b/cli/cmd/testdata/install_custom_registry.golden
index 0cb6c7e03..833df41da 100644
--- a/cli/cmd/testdata/install_custom_registry.golden
+++ b/cli/cmd/testdata/install_custom_registry.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden
index 5250b83c9..f4d2458b1 100644
--- a/cli/cmd/testdata/install_default.golden
+++ b/cli/cmd/testdata/install_default.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_default_override_dst_get_nets.golden b/cli/cmd/testdata/install_default_override_dst_get_nets.golden
index 7606e00b5..245953ff7 100644
--- a/cli/cmd/testdata/install_default_override_dst_get_nets.golden
+++ b/cli/cmd/testdata/install_default_override_dst_get_nets.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_default_token.golden b/cli/cmd/testdata/install_default_token.golden
index 6d6cbe3d0..53f20d305 100644
--- a/cli/cmd/testdata/install_default_token.golden
+++ b/cli/cmd/testdata/install_default_token.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_ha_output.golden b/cli/cmd/testdata/install_ha_output.golden
index 626c1a23c..cd35b25f2 100644
--- a/cli/cmd/testdata/install_ha_output.golden
+++ b/cli/cmd/testdata/install_ha_output.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_ha_with_overrides_output.golden b/cli/cmd/testdata/install_ha_with_overrides_output.golden
index 366b59ee8..16e4b5af7 100644
--- a/cli/cmd/testdata/install_ha_with_overrides_output.golden
+++ b/cli/cmd/testdata/install_ha_with_overrides_output.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_heartbeat_disabled_output.golden b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
index f82d4e0f2..03f28da40 100644
--- a/cli/cmd/testdata/install_heartbeat_disabled_output.golden
+++ b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_no_init_container.golden b/cli/cmd/testdata/install_no_init_container.golden
index 5808466ae..47f7dcd3a 100644
--- a/cli/cmd/testdata/install_no_init_container.golden
+++ b/cli/cmd/testdata/install_no_init_container.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden
index 0f8201a3d..ae1487f78 100644
--- a/cli/cmd/testdata/install_output.golden
+++ b/cli/cmd/testdata/install_output.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: CliVersion
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: CliVersion
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: CliVersion
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_proxy_ignores.golden b/cli/cmd/testdata/install_proxy_ignores.golden
index b7c840d84..f0c6f40ca 100644
--- a/cli/cmd/testdata/install_proxy_ignores.golden
+++ b/cli/cmd/testdata/install_proxy_ignores.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/cli/cmd/testdata/install_values_file.golden b/cli/cmd/testdata/install_values_file.golden
index b67494fae..85912e05b 100644
--- a/cli/cmd/testdata/install_values_file.golden
+++ b/cli/cmd/testdata/install_values_file.golden
@@ -6,7 +6,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -152,7 +152,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: policy.linkerd.io
@@ -441,7 +441,7 @@ metadata:
   annotations:
     linkerd.io/created-by: linkerd/cli dev-undefined
   labels:
-    helm.sh/chart: linkerd-control-plane-1.1.8-edge
+    helm.sh/chart: linkerd-control-plane-1.1.9-edge
     linkerd.io/control-plane-ns: linkerd
 spec:
   group: linkerd.io
diff --git a/controller/api/destination/watcher/profile_watcher_test.go b/controller/api/destination/watcher/profile_watcher_test.go
index d40ce5431..fa6b6242f 100644
--- a/controller/api/destination/watcher/profile_watcher_test.go
+++ b/controller/api/destination/watcher/profile_watcher_test.go
@@ -94,6 +94,8 @@ func TestProfileWatcherUpdates(t *testing.T) {
 
 			actualProfiles := make([]*sp.ServiceProfileSpec, 0)
 
+			listener.mu.RLock()
+			defer listener.mu.RUnlock()
 			for _, profile := range listener.Profiles {
 				if profile == nil {
 					actualProfiles = append(actualProfiles, nil)
diff --git a/controller/api/destination/watcher/test_util.go b/controller/api/destination/watcher/test_util.go
index 8a4052b28..6d7127f65 100644
--- a/controller/api/destination/watcher/test_util.go
+++ b/controller/api/destination/watcher/test_util.go
@@ -3,6 +3,7 @@ package watcher
 import (
 	"encoding/json"
 	"reflect"
+	"sync"
 	"testing"
 
 	sp "github.com/linkerd/linkerd2/controller/gen/apis/serviceprofile/v1alpha2"
@@ -32,6 +33,7 @@ func (dpl *DeletingProfileListener) Update(profile *sp.ServiceProfile) {
 // in a slice.  Useful for unit tests.
 type BufferingProfileListener struct {
 	Profiles []*sp.ServiceProfile
+	mu       sync.RWMutex
 }
 
 // NewBufferingProfileListener creates a new BufferingProfileListener.
@@ -43,6 +45,8 @@ func NewBufferingProfileListener() *BufferingProfileListener {
 
 // Update stores the update in the internal buffer.
 func (bpl *BufferingProfileListener) Update(profile *sp.ServiceProfile) {
+	bpl.mu.Lock()
+	defer bpl.mu.Unlock()
 	bpl.Profiles = append(bpl.Profiles, profile)
 }
 
diff --git a/multicluster/service-mirror/cluster_watcher_mirroring_test.go b/multicluster/service-mirror/cluster_watcher_mirroring_test.go
index fa6e9c0ee..c062e470b 100644
--- a/multicluster/service-mirror/cluster_watcher_mirroring_test.go
+++ b/multicluster/service-mirror/cluster_watcher_mirroring_test.go
@@ -242,6 +242,7 @@ func TestLocalNamespaceCreatedAfterServiceExport(t *testing.T) {
 		log:                     logging.WithFields(logging.Fields{"cluster": clusterName}),
 		eventsQueue:             q,
 		requeueLimit:            0,
+		gatewayAlive:            true,
 		headlessServicesEnabled: true,
 	}
 
@@ -293,6 +294,251 @@ func TestLocalNamespaceCreatedAfterServiceExport(t *testing.T) {
 	}
 }
 
+func TestServiceCreatedGatewayAlive(t *testing.T) {
+	remoteAPI, err := k8s.NewFakeAPI(
+		gatewayAsYaml("gateway", "gateway-ns", "1", "192.0.0.1", "gateway", 888, "gateway-identity", defaultProbePort, defaultProbePath, defaultProbePeriod),
+		remoteServiceAsYaml("svc", "ns", "1", []corev1.ServicePort{}),
+		endpointsAsYaml("svc", "ns", "192.0.0.1", "gateway-identity", []corev1.EndpointPort{}),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	localAPI, err := k8s.NewFakeAPI(
+		namespaceAsYaml("ns"),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	remoteAPI.Sync(nil)
+	localAPI.Sync(nil)
+
+	events := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
+	watcher := RemoteClusterServiceWatcher{
+		link: &multicluster.Link{
+			TargetClusterName:   clusterName,
+			TargetClusterDomain: clusterDomain,
+			GatewayIdentity:     "gateway-identity",
+			GatewayAddress:      "192.0.0.1",
+			GatewayPort:         888,
+			ProbeSpec:           defaultProbeSpec,
+			Selector:            *defaultSelector,
+		},
+		remoteAPIClient: remoteAPI,
+		localAPIClient:  localAPI,
+		log:             logging.WithFields(logging.Fields{"cluster": clusterName}),
+		eventsQueue:     events,
+		requeueLimit:    0,
+		gatewayAlive:    true,
+	}
+
+	events.Add(&RemoteServiceCreated{
+		service: remoteService("svc", "ns", "1", map[string]string{
+			consts.DefaultExportedServiceSelector: "true",
+		}, []corev1.ServicePort{
+			{
+				Name:     "port",
+				Protocol: "TCP",
+				Port:     111,
+			},
+		}),
+	})
+	for events.Len() > 0 {
+		watcher.processNextEvent(context.Background())
+	}
+
+	// Expect Service svc-remote to be created with ready endpoints because
+	// the Namespace ns exists and the gateway is alive.
+	_, err = localAPI.Client.CoreV1().Services("ns").Get(context.Background(), "svc-remote", metav1.GetOptions{})
+	if err != nil {
+		t.Fatalf("error getting svc-remote Service: %v", err)
+	}
+	endpoints, err := localAPI.Client.CoreV1().Endpoints("ns").Get(context.Background(), "svc-remote", metav1.GetOptions{})
+	if err != nil {
+		t.Fatalf("error getting svc-remote Endpoints: %v", err)
+	}
+	if len(endpoints.Subsets) == 0 {
+		t.Fatal("expected svc-remote Endpoints subsets")
+	}
+	for _, ss := range endpoints.Subsets {
+		if len(ss.Addresses) == 0 {
+			t.Fatal("svc-remote Endpoints should have addresses")
+		}
+		if len(ss.NotReadyAddresses) != 0 {
+			t.Fatalf("svc-remote Endpoints should not have not ready addresses: %v", ss.NotReadyAddresses)
+		}
+	}
+
+	// The gateway is now down which triggers repairing Endpoints on the local
+	// cluster.
+	watcher.gatewayAlive = false
+	events.Add(&RepairEndpoints{})
+	for events.Len() > 0 {
+		watcher.processNextEvent(context.Background())
+	}
+
+	// When repairing Endpoints on the local cluster, the gateway address
+	// should have been moved to NotReadyAddresses meaning that Endpoints
+	// for the mirrored Service svc-remote should have no ready addresses.
+	endpoints, err = localAPI.Client.CoreV1().Endpoints("ns").Get(context.Background(), "svc-remote", metav1.GetOptions{})
+	if err != nil {
+		t.Fatalf("error getting svc-remote Endpoints locally: %v", err)
+	}
+	if len(endpoints.Subsets) == 0 {
+		t.Fatal("expected svc-remote Endpoints subsets")
+	}
+	for _, ss := range endpoints.Subsets {
+		if len(ss.NotReadyAddresses) == 0 {
+			t.Fatal("svc-remote Endpoints should have not ready addresses")
+		}
+		if len(ss.Addresses) != 0 {
+			t.Fatalf("svc-remote Endpoints should not have addresses: %v", ss.Addresses)
+		}
+	}
+
+	// Issue an update for the remote Service which adds a new label
+	// 'new-label'. This should exercise RemoteServiceUpdated which should
+	// update svc-remote; the gateway is still not alive though so we expect
+	// the Endpoints of svc-remote to still have no ready addresses.
+	events.Add(&RemoteServiceUpdated{
+		localService:   remoteService("svc-remote", "ns", "2", nil, nil),
+		localEndpoints: endpoints,
+		remoteUpdate: remoteService("svc", "ns", "2", map[string]string{
+			consts.DefaultExportedServiceSelector: "true",
+			"new-label":                           "hi",
+		}, []corev1.ServicePort{
+			{
+				Name:     "port",
+				Protocol: "TCP",
+				Port:     111,
+			},
+		}),
+	})
+	for events.Len() > 0 {
+		watcher.processNextEvent(context.Background())
+	}
+	service, err := localAPI.Client.CoreV1().Services("ns").Get(context.Background(), "svc-remote", metav1.GetOptions{})
+	if err != nil {
+		t.Fatalf("error getting svc-remote Service: %v", err)
+	}
+	_, ok := service.Labels["new-label"]
+	if !ok {
+		t.Fatalf("error updating svc-remote Service: %v", err)
+	}
+	endpoints, err = localAPI.Client.CoreV1().Endpoints("ns").Get(context.Background(), "svc-remote", metav1.GetOptions{})
+	if err != nil {
+		t.Fatalf("error getting svc-remote Endpoints: %v", err)
+	}
+	if len(endpoints.Subsets) == 0 {
+		t.Fatal("expected svc-remote Endpoints subsets")
+	}
+	for _, ss := range endpoints.Subsets {
+		if len(ss.NotReadyAddresses) == 0 {
+			t.Fatal("svc-remote Endpoints should have not ready addresses")
+		}
+		if len(ss.Addresses) != 0 {
+			t.Fatalf("svc-remote Endpoints should not have addresses: %v", ss.Addresses)
+		}
+	}
+}
+
+func TestServiceCreatedGatewayDown(t *testing.T) {
+	remoteAPI, err := k8s.NewFakeAPI(
+		gatewayAsYaml("gateway", "gateway-ns", "1", "192.0.0.1", "gateway", 888, "gateway-identity", defaultProbePort, defaultProbePath, defaultProbePeriod),
+		remoteServiceAsYaml("svc", "ns", "1", []corev1.ServicePort{}),
+		endpointsAsYaml("svc", "ns", "192.0.0.1", "gateway-identity", []corev1.EndpointPort{}),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	localAPI, err := k8s.NewFakeAPI(
+		namespaceAsYaml("ns"),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	remoteAPI.Sync(nil)
+	localAPI.Sync(nil)
+
+	events := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
+	watcher := RemoteClusterServiceWatcher{
+		link: &multicluster.Link{
+			TargetClusterName:   clusterName,
+			TargetClusterDomain: clusterDomain,
+			GatewayIdentity:     "gateway-identity",
+			GatewayAddress:      "192.0.0.1",
+			GatewayPort:         888,
+			ProbeSpec:           defaultProbeSpec,
+			Selector:            *defaultSelector,
+		},
+		remoteAPIClient: remoteAPI,
+		localAPIClient:  localAPI,
+		log:             logging.WithFields(logging.Fields{"cluster": clusterName}),
+		eventsQueue:     events,
+		requeueLimit:    0,
+		gatewayAlive:    false,
+	}
+
+	events.Add(&RemoteServiceCreated{
+		service: remoteService("svc", "ns", "1", map[string]string{
+			consts.DefaultExportedServiceSelector: "true",
+		}, []corev1.ServicePort{
+			{
+				Name:     "port",
+				Protocol: "TCP",
+				Port:     111,
+			},
+		}),
+	})
+	for events.Len() > 0 {
+		watcher.processNextEvent(context.Background())
+	}
+
+	// Expect Service svc-remote to be created with Endpoints subsets
+	// that are not ready because the gateway is down.
+	_, err = localAPI.Client.CoreV1().Services("ns").Get(context.Background(), "svc-remote", metav1.GetOptions{})
+	if err != nil {
+		t.Fatalf("error getting svc-remote Service: %v", err)
+	}
+	endpoints, err := localAPI.Client.CoreV1().Endpoints("ns").Get(context.Background(), "svc-remote", metav1.GetOptions{})
+	if err != nil {
+		t.Fatalf("error getting svc-remote Endpoints: %v", err)
+	}
+	if len(endpoints.Subsets) == 0 {
+		t.Fatal("expected svc-remote Endpoints subsets")
+	}
+	for _, ss := range endpoints.Subsets {
+		if len(ss.NotReadyAddresses) == 0 {
+			t.Fatal("svc-remote Endpoints should have not ready addresses")
+		}
+		if len(ss.Addresses) != 0 {
+			t.Fatalf("svc-remote Endpoints should not have addresses: %v", ss.Addresses)
+		}
+	}
+
+	// The gateway is now alive which triggers repairing Endpoints on the
+	// local cluster.
+	watcher.gatewayAlive = true
+	events.Add(&RepairEndpoints{})
+	for events.Len() > 0 {
+		watcher.processNextEvent(context.Background())
+	}
+	endpoints, err = localAPI.Client.CoreV1().Endpoints("ns").Get(context.Background(), "svc-remote", metav1.GetOptions{})
+	if err != nil {
+		t.Fatalf("error getting svc-remote Endpoints locally: %v", err)
+	}
+	if len(endpoints.Subsets) == 0 {
+		t.Fatal("expected svc-remote Endpoints subsets")
+	}
+	for _, ss := range endpoints.Subsets {
+		if len(ss.Addresses) == 0 {
+			t.Fatal("svc-remote Endpoints should have addresses")
+		}
+		if len(ss.NotReadyAddresses) != 0 {
+			t.Fatalf("svc-remote Service endpoints should not have not ready addresses: %v", ss.NotReadyAddresses)
+		}
+	}
+}
+
 func TestRemoteServiceDeletedMirroring(t *testing.T) {
 	for _, tt := range []mirroringTestCase{
 		{
diff --git a/multicluster/service-mirror/cluster_watcher_test_util.go b/multicluster/service-mirror/cluster_watcher_test_util.go
index a276a12eb..f811eaf8e 100644
--- a/multicluster/service-mirror/cluster_watcher_test_util.go
+++ b/multicluster/service-mirror/cluster_watcher_test_util.go
@@ -62,6 +62,7 @@ func (te *testEnvironment) runEnvironment(watcherQueue workqueue.RateLimitingInt
 		log:                     logging.WithFields(logging.Fields{"cluster": clusterName}),
 		eventsQueue:             watcherQueue,
 		requeueLimit:            0,
+		gatewayAlive:            true,
 		headlessServicesEnabled: true,
 	}