From 17440df5265e43331015c463abf5de3644352357 Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Tue, 22 Sep 2026 12:47:35 -0600 Subject: [PATCH] test: build e2e cleanup client from helm's own settings Overriding only HELM_KUBECONTEXT still left the cleanup client able to diverge from the helm subprocesses, which also honor HELM_KUBEAPISERVER, HELM_KUBETOKEN, HELM_KUBECAFILE, HELM_KUBETLS_SERVER_NAME and HELM_KUBEINSECURE_SKIP_TLS_VERIFY. Any of those could send the namespace delete to a different endpoint, where NotFound reads as success and the namespace leaks. Resolve the client through cli.New().RESTClientGetter() instead, so it is built exactly the way the subprocesses build theirs and stays in step without mirroring each variable by hand. Signed-off-by: Terry Howe --- test/e2e/helper_test.go | 4 ---- test/e2e/oci_test.go | 27 +++++++++++---------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/test/e2e/helper_test.go b/test/e2e/helper_test.go index f869224c9..82770323a 100644 --- a/test/e2e/helper_test.go +++ b/test/e2e/helper_test.go @@ -65,10 +65,6 @@ const ( envKubernetes = "HELM_E2E_KUBERNETES" // envNamespace is the namespace the Kubernetes tests install into. envNamespace = "HELM_E2E_NAMESPACE" - // envHelmKubeContext is helm's own kubecontext variable. The tests do not - // set it, but helm subprocesses inherit it, so cleanup performed through - // the Kubernetes API has to honor it too. - envHelmKubeContext = "HELM_KUBECONTEXT" ) // harness carries the resolved configuration shared by the end-to-end tests. diff --git a/test/e2e/oci_test.go b/test/e2e/oci_test.go index 1a841f8cb..971d0d986 100644 --- a/test/e2e/oci_test.go +++ b/test/e2e/oci_test.go @@ -37,7 +37,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" + + "helm.sh/helm/v4/pkg/cli" ) // TestOCIRegistryPushPull pushes chart archives to the configured OCI registry @@ -370,21 +371,15 @@ func archiveFiles(t *testing.T, path string) map[string][]byte { func deleteNamespace(t *testing.T, namespace string) { t.Helper() - // Select the same context the helm subprocesses used. They inherit - // HELM_KUBECONTEXT from the environment, so without this override the - // releases would be installed into one cluster while the namespace was - // deleted from whichever cluster the kubeconfig's current-context names. - // A delete against the wrong cluster returns NotFound, which would look - // like success while leaking the namespace. - overrides := &clientcmd.ConfigOverrides{} - if kubeContext := os.Getenv(envHelmKubeContext); kubeContext != "" { - overrides.CurrentContext = kubeContext - } - - cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - clientcmd.NewDefaultClientConfigLoadingRules(), - overrides, - ).ClientConfig() + // Resolve the cluster exactly the way the helm subprocesses do. They read + // their Kubernetes configuration from the environment (HELM_KUBECONTEXT, + // HELM_KUBEAPISERVER, HELM_KUBETOKEN, HELM_KUBECAFILE and friends), so + // building this client any other way risks installing releases into one + // cluster and sending the namespace delete to another. Such a delete + // returns NotFound, which would look like success while leaking the + // namespace. Going through helm's own settings keeps the two in step + // without having to mirror each variable here. + cfg, err := cli.New().RESTClientGetter().ToRESTConfig() if err != nil { t.Errorf("building kube client config to delete namespace %s: %v", namespace, err) return