Repository: linkerd/linkerd2
Pull request #6681 (2021-08-16 17:03:18)

Bump history from 5.0.0 to 5.0.1 in /web/app
Bumps [history](https://github.com/ReactTraining/history) from 5.0.0 to 5.0.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/ReactTraining/history/releases">history's releases</a>.</em></p>
<blockquote>
<h2>v5.0.1</h2>
<p>This patch release contains a <em>tiny</em> TypeScript update to use the built-in <code>Partial</code> utility for <code>PartialPath</code> and <code>PartialLocation</code>. We always love it when we can ship just a little less code!</p>
<h2>🙏 Credits</h2>
<p>Thanks to <a href="https://github.com/liuhanqu"><code>@​liuhanqu</code></a>, <a href="https://github.com/hanquliu"><code>@​hanquliu</code></a>, <a href="https://github.com/chaance"><code>@​chaance</code></a> and <a href="https://github.com/mjackson"><code>@​mjackson</code></a> for your contributions!</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/remix-run/history/commit/bb756f4ba2da9a6a9edbd6d716b1c2b5575621f8"><code>bb756f4</code></a> Version 5.0.1</li>
<li><a href="https://github.com/remix-run/history/commit/f042c815d9060bce08cdfcf37a93c1cce71ab1a8"><code>f042c81</code></a> use npm7 in release action</li>
<li><a href="https://github.com/remix-run/history/commit/e7e8a4326b026175886f44b3afd9135c50133dd8"><code>e7e8a43</code></a> use npm7 in test action</li>
<li><a href="https://github.com/remix-run/history/commit/c1c3de58ca94b0f3a3bcf9953ccb6fbacbabcafe"><code>c1c3de5</code></a> install prettier as devDependency</li>
<li><a href="https://github.com/remix-run/history/commit/eb6a13f1cf2398888ef3f9daf9adf698a29931b0"><code>eb6a13f</code></a> use build-in partial (<a href="https://github-redirect.dependabot.com/ReactTraining/history/issues/839">#839</a>)</li>
<li><a href="https://github.com/remix-run/history/commit/488fbb42462392c6d7e57930e45822ed42ca7b7b"><code>488fbb4</code></a> Travis &gt; GitHub actions (<a href="https://github-redirect.dependabot.com/ReactTraining/history/issues/888">#888</a>)</li>
<li><a href="https://github.com/remix-run/history/commit/a13841e02b28be6e712d124cb10ca395b2c0d4a6"><code>a13841e</code></a> Dev environment updates (<a href="https://github-redirect.dependabot.com/ReactTraining/history/issues/886">#886</a>)</li>
<li><a href="https://github.com/remix-run/history/commit/a33446891e9a99102fffa72f068e8dfa2b6e1fa2"><code>a334468</code></a> use build-in partial (<a href="https://github-redirect.dependabot.com/ReactTraining/history/issues/839">#839</a>)</li>
<li><a href="https://github.com/remix-run/history/commit/28c89f4091ae9e1b0001341ea60c629674e83627"><code>28c89f4</code></a> Put type declarations in collapsible blocks</li>
<li>See full diff in <a href="https://github.com/ReactTraining/history/compare/v5.0.0...v5.0.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=history&package-manager=npm_and_yarn&previous-version=5.0.0&new-version=5.0.1)](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/pkg/k8s/portforward.go b/pkg/k8s/portforward.go
index 7dea912f2..554a227b4 100644
--- a/pkg/k8s/portforward.go
+++ b/pkg/k8s/portforward.go
@@ -124,7 +124,17 @@ func newPortForward(
 	emitLogs bool,
 ) (*PortForward, error) {
 
-	req := k8sAPI.CoreV1().RESTClient().Post().
+	restClient := k8sAPI.CoreV1().RESTClient()
+	// This early return is for testing purposes. If the k8sAPI is a fake
+	// client, attempting to create a request will result in a nil-pointer
+	// panic. Instead, we return with no port-forward and no error.
+	if fakeRest, ok := restClient.(*rest.RESTClient); ok {
+		if fakeRest == nil {
+			return nil, nil
+		}
+	}
+
+	req := restClient.Post().
 		Resource("pods").
 		Namespace(namespace).
 		Name(podName).
diff --git a/web/app/package.json b/web/app/package.json
index ab6a56fe5..7f0f6c91f 100644
--- a/web/app/package.json
+++ b/web/app/package.json
@@ -68,7 +68,7 @@
     "eslint-plugin-react-hooks": "^4.2.0",
     "eslint-webpack-plugin": "^3.0.1",
     "file-loader": "^6.2.0",
-    "history": "5.0.0",
+    "history": "5.0.1",
     "html-webpack-plugin": "^5.3.2",
     "jest": "^27.0.6",
     "jest-enzyme": "7.1.2",
diff --git a/web/app/yarn.lock b/web/app/yarn.lock
index aceef00fe..048ed0f2e 100644
--- a/web/app/yarn.lock
+++ b/web/app/yarn.lock
@@ -4756,10 +4756,10 @@ he@^1.2.0:
   resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
   integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
 
-history@5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/history/-/history-5.0.0.tgz#0cabbb6c4bbf835addb874f8259f6d25101efd08"
-  integrity sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg==
+history@5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/history/-/history-5.0.1.tgz#de35025ed08bce0db62364b47ebbf9d97b5eb06a"
+  integrity sha512-5qC/tFUKfVci5kzgRxZxN5Mf1CV8NmJx9ByaPX0YTLx5Vz3Svh7NYp6eA4CpDq4iA9D0C1t8BNIfvQIrUI3mVw==
   dependencies:
     "@babel/runtime" "^7.7.6"
 


Test patch:
diff --git a/cli/cmd/metrics_test.go b/cli/cmd/metrics_test.go
new file mode 100644
index 000000000..d28cda687
--- /dev/null
+++ b/cli/cmd/metrics_test.go
@@ -0,0 +1,130 @@
+package cmd
+
+import (
+	"context"
+	"testing"
+
+	"github.com/linkerd/linkerd2/pkg/k8s"
+)
+
+func TestGetPodsFor(t *testing.T) {
+
+	configs := []string{
+		// pod-1
+		`apiVersion: v1
+kind: Pod
+metadata:
+  name: pod-1
+  namespace: ns
+  uid: pod-1
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: ReplicaSet
+    name: rs-1
+    uid: rs-1
+`,
+		// rs-1
+		`apiVersion: apps/v1
+kind: ReplicaSet
+metadata:
+  name: rs-1
+  namespace: ns
+  uid: rs-1
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: Deployment
+    name: deploy-1
+    uid: deploy-1
+spec:
+  selector:
+    matchLabels:
+      app: foo
+`,
+		// deploy-1
+		`apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: deploy-1
+  namespace: ns
+  uid: deploy-1
+spec:
+  selector:
+    matchLabels:
+      app: foo
+`,
+		// pod-2
+		`apiVersion: v1
+kind: Pod
+metadata:
+  name: pod-2
+  namespace: ns
+  uid: pod-2
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: ReplicaSet
+    name: rs-2
+    uid: rs-2
+`,
+		// rs-2
+		`apiVersion: apps/v1
+kind: ReplicaSet
+metadata:
+  name: rs-2
+  namespace: ns
+  uid: rs-2
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: Deployment
+    name: deploy-2
+    uid: deploy-2
+spec:
+  selector:
+    matchLabels:
+     app: foo
+`,
+		// deploy-2
+		`apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: deploy-2
+  namespace: ns
+  uid: deploy-2
+spec:
+  selector:
+    matchLabels:
+      app: foo
+`}
+
+	k8sClient, err := k8s.NewFakeAPI(configs...)
+	if err != nil {
+		t.Fatalf("Unexpected error %s", err)
+	}
+
+	// Both pod-1 and pod-2 have labels which match deploy-1's selector.
+	// However, only pod-1 is owned by deploy-1 according to the owner references.
+	// Owner references should be considered authoritative to resolve ambiguity
+	// when deployments have overlapping seletors.
+	pods, err := getPodsFor(context.Background(), k8sClient, "ns", "deploy/deploy-1")
+	if err != nil {
+		t.Fatalf("Unexpected error %s", err)
+	}
+
+	if len(pods) != 1 {
+		for _, p := range pods {
+			t.Logf("%s/%s", p.Namespace, p.Name)
+		}
+		t.Fatalf("Expected 1 pod, got %d", len(pods))
+	}
+}
diff --git a/pkg/k8s/portforward_test.go b/pkg/k8s/portforward_test.go
index 989070e81..e1a169b98 100644
--- a/pkg/k8s/portforward_test.go
+++ b/pkg/k8s/portforward_test.go
@@ -69,57 +69,177 @@ spec:
 }
 
 func TestNewPortForward(t *testing.T) {
-	// TODO: test successful cases by mocking out `clientset.CoreV1().RESTClient()`
 	tests := []struct {
-		ns         string
-		deployName string
-		k8sConfigs []string
-		err        error
+		description string
+		ns          string
+		deployName  string
+		k8sConfigs  []string
+		err         error
 	}{
 		{
-			"pod-ns",
-			"deploy-name",
+			"Pod is owned by the specified deployment",
+			"ns",
+			"deploy",
 			[]string{`apiVersion: v1
 kind: Pod
 metadata:
-  name: bad-name
-  namespace: pod-ns
+  name: pod
+  namespace: ns
+  uid: pod
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: Deployment
+    name: rs
+    uid: rs
 status:
   phase: Running`,
-			},
-			errors.New("no running pods found for deploy-name"),
+				`apiVersion: apps/v1
+kind: ReplicaSet
+metadata:
+  name: rs
+  namespace: ns
+  uid: rs
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: Deployment
+    name: deploy
+    uid: deploy
+  spec:
+    selector:
+      matchLabels:
+        app: foo
+`,
+				`apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: deploy
+  namespace: ns
+  uid: deploy
+spec:
+  selector:
+    matchLabels:
+      app: foo
+`},
+			nil,
 		},
+		// In the case of overlapping deployments, a pod may match the label
+		// selector of more than one deployment but will still be owned by
+		// exactly one.
 		{
-			"pod-ns",
-			"deploy-name",
+			"Pod's labels match, but is not owned by the deployment",
+			"ns",
+			"deploy",
 			[]string{`apiVersion: v1
 kind: Pod
 metadata:
-  name: deploy-name-foo-bar
-  namespace: bad-ns
-status:
-  phase: Running`,
-			},
-			errors.New("no running pods found for deploy-name"),
+  name: pod
+  namespace: ns
+  uid: pod
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: ReplicaSet
+    name: rs
+    uid: SOME-OTHER-UID
+  status:
+    phase: Running`,
+				`apiVersion: apps/v1
+kind: ReplicaSet
+metadata:
+  name: rs
+  namespace: ns
+  uid: rs
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: Deployment
+    name: deploy
+    uid: deploy
+  spec:
+    selector:
+      matchLabels:
+        app: foo
+`,
+				`apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: deploy
+  namespace: ns
+  uid: deploy
+spec:
+  selector:
+    matchLabels:
+      app: foo
+`},
+			errors.New("no running pods found for deploy"),
 		},
 		{
-			"pod-ns",
-			"deploy-name",
+			"Pod is owned by the specified deployment but is not running",
+			"ns",
+			"deploy",
 			[]string{`apiVersion: v1
 kind: Pod
 metadata:
-  name: deploy-name-foo-bar
-  namespace: pod-ns
-status:
-  phase: Stopped`,
-			},
-			errors.New("no running pods found for deploy-name"),
+  name: pod
+  namespace: ns
+  uid: pod
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: ReplicaSet
+    name: rs
+    uid: rs
+  status:
+    phase: Stopped`,
+				`apiVersion: apps/v1
+kind: ReplicaSet
+metadata:
+  name: rs
+  namespace: ns
+  uid: rs
+  labels:
+    app: foo
+  ownerReferences:
+  - apiVersion: apps/v1
+    controller: true
+    kind: Deployment
+    name: deploy
+    uid: deploy
+spec:
+  selector:
+    matchLabels:
+      app: foo
+`,
+				`apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: deploy
+  namespace: ns
+  uid: deploy
+spec:
+  selector:
+    matchLabels:
+      app: foo
+`},
+			errors.New("no running pods found for deploy"),
 		},
 	}
 
-	for i, test := range tests {
+	for _, test := range tests {
 		test := test // pin
-		t.Run(fmt.Sprintf("%d: NewPortForward returns expected result", i), func(t *testing.T) {
+		t.Run(test.description, func(t *testing.T) {
 			k8sClient, err := NewFakeAPI(test.k8sConfigs...)
 			if err != nil {
 				t.Fatalf("Unexpected error %s", err)