Skip to content

Commit 1df6ec6

Browse files
✨ (go/v4): Add optional kubectl context locking for e2e tests
E2E tests now support optional context locking via KUBE_CONTEXT environment variable. When set, tests validate and lock to the specified kubectl context, preventing context switching during execution. Features: - Display current kubectl context at test startup - Validate context matches KUBE_CONTEXT if set - Add --context flag to all kubectl commands when locked - 100% backward compatible (opt-in feature) Usage: KUBE_CONTEXT=kind-test make test-e2e This addresses feedback from PR #5329 about preventing inadvertent context switching during test execution, while maintaining ease of use (works without any env vars by default). Assisted-by: Cursor
1 parent 50a1cd0 commit 1df6ec6

File tree

17 files changed

+216
-22
lines changed

17 files changed

+216
-22
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ test-testdata: ## Run the tests of the testdata directory
172172
.PHONY: test-e2e-local
173173
test-e2e-local: ## Run the end-to-end tests locally
174174
## To keep the same kind cluster between test runs, use `SKIP_KIND_CLEANUP=1 make test-e2e-local`
175+
## To lock to a specific kubectl context, use `KUBE_CONTEXT=kind-test make test-e2e-local`
175176
./test/e2e/local.sh
176177

177178
.PHONY: test-e2e-ci

docs/book/src/cronjob-tutorial/testdata/project/Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,11 @@ test: manifests generate fmt vet setup-envtest ## Run tests.
6767

6868
# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
6969
# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
70-
# CertManager is installed by default; skip with:
71-
# - CERT_MANAGER_INSTALL_SKIP=true
70+
# Environment variables:
71+
# - KIND_CLUSTER: Name of the Kind cluster (default: project-test-e2e)
72+
# - KUBE_CONTEXT: Kubectl context to use (default: current-context)
73+
# - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
74+
# Note: When KIND_CLUSTER=my-cluster, the kubectl context will be "kind-my-cluster"
7275
KIND_CLUSTER ?= project-test-e2e
7376

7477
.PHONY: setup-test-e2e

docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"os"
2525
"os/exec"
26+
"strings"
2627
"testing"
2728

2829
. "github.com/onsi/ginkgo/v2"
@@ -43,7 +44,12 @@ var (
4344
// TestE2E runs the e2e test suite to validate the solution in an isolated environment.
4445
// The default setup requires Kind and CertManager.
4546
//
46-
// To skip CertManager installation, set: CERT_MANAGER_INSTALL_SKIP=true
47+
// Environment variables (see Makefile target 'test-e2e' for examples):
48+
// - KIND_CLUSTER: Name of the Kind cluster (default: kind)
49+
// - KUBE_CONTEXT: Kubectl context to use (default: current-context)
50+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
51+
//
52+
// Note: When KIND_CLUSTER=my-cluster, the kubectl context will be "kind-my-cluster"
4753
func TestE2E(t *testing.T) {
4854
RegisterFailHandler(Fail)
4955
_, _ = fmt.Fprintf(GinkgoWriter, "Starting project e2e test suite\n")
@@ -54,6 +60,13 @@ var _ = BeforeSuite(func() {
5460
By("Ensure that Prometheus is enabled")
5561
_ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#")
5662

63+
// Display kubectl context being used
64+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
65+
_, _ = fmt.Fprintf(GinkgoWriter, "Using context: %s\n", kubectx)
66+
} else if currentCtx, err := getCurrentContext(); err == nil {
67+
_, _ = fmt.Fprintf(GinkgoWriter, "Using context: %s\n", currentCtx)
68+
}
69+
5770
By("building the manager image")
5871
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
5972
_, err := utils.Run(cmd)
@@ -116,3 +129,13 @@ func teardownCertManager() {
116129
By("uninstalling CertManager")
117130
utils.UninstallCertManager()
118131
}
132+
133+
// getCurrentContext returns the current kubectl context name
134+
func getCurrentContext() (string, error) {
135+
cmd := exec.Command("kubectl", "config", "current-context")
136+
output, err := cmd.Output()
137+
if err != nil {
138+
return "", err
139+
}
140+
return strings.TrimSpace(string(output)), nil
141+
}

docs/book/src/getting-started/testdata/project/Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,11 @@ test: manifests generate fmt vet setup-envtest ## Run tests.
6363

6464
# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
6565
# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
66-
# CertManager is installed by default; skip with:
67-
# - CERT_MANAGER_INSTALL_SKIP=true
66+
# Environment variables:
67+
# - KIND_CLUSTER: Name of the Kind cluster (default: project-test-e2e)
68+
# - KUBE_CONTEXT: Kubectl context to use (default: current-context)
69+
# - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
70+
# Note: When KIND_CLUSTER=my-cluster, the kubectl context will be "kind-my-cluster"
6871
KIND_CLUSTER ?= project-test-e2e
6972

7073
.PHONY: setup-test-e2e

docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"os"
2525
"os/exec"
26+
"strings"
2627
"testing"
2728

2829
. "github.com/onsi/ginkgo/v2"
@@ -41,14 +42,26 @@ var (
4142
// TestE2E runs the e2e test suite to validate the solution in an isolated environment.
4243
// The default setup requires Kind and CertManager.
4344
//
44-
// To skip CertManager installation, set: CERT_MANAGER_INSTALL_SKIP=true
45+
// Environment variables (see Makefile target 'test-e2e' for examples):
46+
// - KIND_CLUSTER: Name of the Kind cluster (default: kind)
47+
// - KUBE_CONTEXT: Kubectl context to use (default: current-context)
48+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
49+
//
50+
// Note: When KIND_CLUSTER=my-cluster, the kubectl context will be "kind-my-cluster"
4551
func TestE2E(t *testing.T) {
4652
RegisterFailHandler(Fail)
4753
_, _ = fmt.Fprintf(GinkgoWriter, "Starting project e2e test suite\n")
4854
RunSpecs(t, "e2e suite")
4955
}
5056

5157
var _ = BeforeSuite(func() {
58+
// Display kubectl context being used
59+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
60+
_, _ = fmt.Fprintf(GinkgoWriter, "Using context: %s\n", kubectx)
61+
} else if currentCtx, err := getCurrentContext(); err == nil {
62+
_, _ = fmt.Fprintf(GinkgoWriter, "Using context: %s\n", currentCtx)
63+
}
64+
5265
By("building the manager image")
5366
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
5467
_, err := utils.Run(cmd)
@@ -96,3 +109,13 @@ func teardownCertManager() {
96109
By("uninstalling CertManager")
97110
utils.UninstallCertManager()
98111
}
112+
113+
// getCurrentContext returns the current kubectl context name
114+
func getCurrentContext() (string, error) {
115+
cmd := exec.Command("kubectl", "config", "current-context")
116+
output, err := cmd.Output()
117+
if err != nil {
118+
return "", err
119+
}
120+
return strings.TrimSpace(string(output)), nil
121+
}

docs/book/src/multiversion-tutorial/testdata/project/Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,11 @@ test: manifests generate fmt vet setup-envtest ## Run tests.
6767

6868
# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
6969
# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
70-
# CertManager is installed by default; skip with:
71-
# - CERT_MANAGER_INSTALL_SKIP=true
70+
# Environment variables:
71+
# - KIND_CLUSTER: Name of the Kind cluster (default: project-test-e2e)
72+
# - KUBE_CONTEXT: Kubectl context to use (default: current-context)
73+
# - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
74+
# Note: When KIND_CLUSTER=my-cluster, the kubectl context will be "kind-my-cluster"
7275
KIND_CLUSTER ?= project-test-e2e
7376

7477
.PHONY: setup-test-e2e

docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_suite_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"os"
2525
"os/exec"
26+
"strings"
2627
"testing"
2728

2829
. "github.com/onsi/ginkgo/v2"
@@ -43,7 +44,12 @@ var (
4344
// TestE2E runs the e2e test suite to validate the solution in an isolated environment.
4445
// The default setup requires Kind and CertManager.
4546
//
46-
// To skip CertManager installation, set: CERT_MANAGER_INSTALL_SKIP=true
47+
// Environment variables (see Makefile target 'test-e2e' for examples):
48+
// - KIND_CLUSTER: Name of the Kind cluster (default: kind)
49+
// - KUBE_CONTEXT: Kubectl context to use (default: current-context)
50+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
51+
//
52+
// Note: When KIND_CLUSTER=my-cluster, the kubectl context will be "kind-my-cluster"
4753
func TestE2E(t *testing.T) {
4854
RegisterFailHandler(Fail)
4955
_, _ = fmt.Fprintf(GinkgoWriter, "Starting project e2e test suite\n")
@@ -54,6 +60,13 @@ var _ = BeforeSuite(func() {
5460
By("Ensure that Prometheus is enabled")
5561
_ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#")
5662

63+
// Display kubectl context being used
64+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
65+
_, _ = fmt.Fprintf(GinkgoWriter, "Using context: %s\n", kubectx)
66+
} else if currentCtx, err := getCurrentContext(); err == nil {
67+
_, _ = fmt.Fprintf(GinkgoWriter, "Using context: %s\n", currentCtx)
68+
}
69+
5770
By("building the manager image")
5871
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
5972
_, err := utils.Run(cmd)
@@ -116,3 +129,13 @@ func teardownCertManager() {
116129
By("uninstalling CertManager")
117130
utils.UninstallCertManager()
118131
}
132+
133+
// getCurrentContext returns the current kubectl context name
134+
func getCurrentContext() (string, error) {
135+
cmd := exec.Command("kubectl", "config", "current-context")
136+
output, err := cmd.Output()
137+
if err != nil {
138+
return "", err
139+
}
140+
return strings.TrimSpace(string(output)), nil
141+
}

pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,11 @@ test: manifests generate fmt vet setup-envtest ## Run tests.
142142
143143
# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
144144
# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
145-
# CertManager is installed by default; skip with:
146-
# - CERT_MANAGER_INSTALL_SKIP=true
145+
# Environment variables:
146+
# - KIND_CLUSTER: Name of the Kind cluster (default: {{ .ProjectName }}-test-e2e)
147+
# - KUBE_CONTEXT: Kubectl context to use (default: current-context)
148+
# - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
149+
# Note: When KIND_CLUSTER=my-cluster, the kubectl context will be "kind-my-cluster"
147150
KIND_CLUSTER ?= {{ .ProjectName }}-test-e2e
148151
149152
.PHONY: setup-test-e2e

pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/suite.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import (
5151
"fmt"
5252
"os"
5353
"os/exec"
54+
"strings"
5455
"testing"
5556
5657
. "github.com/onsi/ginkgo/v2"
@@ -69,14 +70,26 @@ var (
6970
// TestE2E runs the e2e test suite to validate the solution in an isolated environment.
7071
// The default setup requires Kind and CertManager.
7172
//
72-
// To skip CertManager installation, set: CERT_MANAGER_INSTALL_SKIP=true
73+
// Environment variables (see Makefile target 'test-e2e' for examples):
74+
// - KIND_CLUSTER: Name of the Kind cluster (default: kind)
75+
// - KUBE_CONTEXT: Kubectl context to use (default: current-context)
76+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
77+
//
78+
// Note: When KIND_CLUSTER=my-cluster, the kubectl context will be "kind-my-cluster"
7379
func TestE2E(t *testing.T) {
7480
RegisterFailHandler(Fail)
7581
_, _ = fmt.Fprintf(GinkgoWriter, "Starting {{ .ProjectName }} e2e test suite\n")
7682
RunSpecs(t, "e2e suite")
7783
}
7884
7985
var _ = BeforeSuite(func() {
86+
// Display kubectl context being used
87+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
88+
_, _ = fmt.Fprintf(GinkgoWriter, "Using context: %s\n", kubectx)
89+
} else if currentCtx, err := getCurrentContext(); err == nil {
90+
_, _ = fmt.Fprintf(GinkgoWriter, "Using context: %s\n", currentCtx)
91+
}
92+
8093
By("building the manager image")
8194
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
8295
_, err := utils.Run(cmd)
@@ -124,4 +137,14 @@ func teardownCertManager() {
124137
By("uninstalling CertManager")
125138
utils.UninstallCertManager()
126139
}
140+
141+
// getCurrentContext returns the current kubectl context name
142+
func getCurrentContext() (string, error) {
143+
cmd := exec.Command("kubectl", "config", "current-context")
144+
output, err := cmd.Output()
145+
if err != nil {
146+
return "", err
147+
}
148+
return strings.TrimSpace(string(output)), nil
149+
}
127150
`

test/e2e/utils/kubectl.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,20 @@ type Kubectl struct {
3030
*CmdContext
3131
Namespace string
3232
ServiceAccount string
33+
KubeContext string
34+
}
35+
36+
// cmdOptionsWithContext prepends --context flag to kubectl commands if KubeContext is set
37+
func (k *Kubectl) cmdOptionsWithContext(cmdOptions ...string) []string {
38+
if k.KubeContext != "" {
39+
return append([]string{"--context", k.KubeContext}, cmdOptions...)
40+
}
41+
return cmdOptions
3342
}
3443

3544
// Command is a general func to run kubectl commands
3645
func (k *Kubectl) Command(cmdOptions ...string) (string, error) {
37-
cmd := exec.Command("kubectl", cmdOptions...)
46+
cmd := exec.Command("kubectl", k.cmdOptionsWithContext(cmdOptions...)...)
3847
output, err := k.Run(cmd)
3948
return string(output), err
4049
}

0 commit comments

Comments
 (0)