From e807d7559fc9a836cc1e86e379cf8e956c68e5c8 Mon Sep 17 00:00:00 2001 From: Casey Davenport Date: Thu, 16 Apr 2026 13:42:17 -0700 Subject: [PATCH] Return error channel from PortForward The port-forward goroutine previously swallowed errors silently. Return a channel so callers can detect when the port-forward process exits and react (e.g., restart it or fail fast with a clear message). --- e2e/pkg/tests/policy/staged_policy.go | 2 +- e2e/pkg/utils/kubectl.go | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/e2e/pkg/tests/policy/staged_policy.go b/e2e/pkg/tests/policy/staged_policy.go index 97db40f7906..2b22e80f823 100644 --- a/e2e/pkg/tests/policy/staged_policy.go +++ b/e2e/pkg/tests/policy/staged_policy.go @@ -93,7 +93,7 @@ var _ = describe.CalicoDescribe( stopCh = make(chan time.Time, 1) // We read flow logs from whisker-backend, we start port forward so we can query the flows - localPort, err := kubectl.PortForward("calico-system", "deployment/whisker", "3002", "", stopCh) + localPort, _, err := kubectl.PortForward("calico-system", "deployment/whisker", "3002", "", stopCh) Expect(err).NotTo(HaveOccurred()) // Build url to get flows from whisker diff --git a/e2e/pkg/utils/kubectl.go b/e2e/pkg/utils/kubectl.go index 55eb74a1814..01bbb5e4a38 100644 --- a/e2e/pkg/utils/kubectl.go +++ b/e2e/pkg/utils/kubectl.go @@ -36,11 +36,13 @@ func (k *Kubectl) Wait(kind, ns, name, user, condition string, timeout time.Dura } // PortForward starts a kubectl port-forward in the background, allocating a random -// local port to avoid conflicts when tests run in parallel. It returns the local port. -func (k *Kubectl) PortForward(ns, pod, remotePort, user string, timeOut chan time.Time) (int, error) { +// local port to avoid conflicts when tests run in parallel. It returns the local port +// and a channel that receives an error (or nil) when the port-forward process exits. +// Callers can select on the channel to detect unexpected port-forward termination. +func (k *Kubectl) PortForward(ns, pod, remotePort, user string, timeOut chan time.Time) (int, <-chan error, error) { localPort, err := getFreePort() if err != nil { - return 0, fmt.Errorf("failed to allocate local port: %w", err) + return 0, nil, fmt.Errorf("failed to allocate local port: %w", err) } options := []string{"port-forward", pod, fmt.Sprintf("%d:%s", localPort, remotePort)} @@ -48,13 +50,13 @@ func (k *Kubectl) PortForward(ns, pod, remotePort, user string, timeOut chan tim options = append(options, fmt.Sprintf("--as=%v", user)) } + errCh := make(chan error, 1) go func() { + defer close(errCh) _, err := kubectl.NewKubectlCommand(ns, options...).WithTimeout(timeOut).Exec() - if err != nil { - return - } + errCh <- err }() - return localPort, nil + return localPort, errCh, nil } func getFreePort() (int, error) {