Skip to content

Commit 049342e

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 507b1a7 commit 049342e

10 files changed

Lines changed: 230 additions & 8 deletions

File tree

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/test/e2e/e2e_suite_test.go

Lines changed: 31 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,9 @@ 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:
48+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
49+
// - KUBE_CONTEXT=<context-name>: (Optional) Lock to specific kubectl context
4750
func TestE2E(t *testing.T) {
4851
RegisterFailHandler(Fail)
4952
_, _ = fmt.Fprintf(GinkgoWriter, "Starting project e2e test suite\n")
@@ -54,6 +57,23 @@ var _ = BeforeSuite(func() {
5457
By("Ensure that Prometheus is enabled")
5558
_ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#")
5659

60+
// Display current kubectl context
61+
currentCtx, err := getCurrentContext()
62+
if err == nil {
63+
_, _ = fmt.Fprintf(GinkgoWriter, "Using kubectl context: %s\n", currentCtx)
64+
}
65+
66+
// Validate context if KUBE_CONTEXT env var is set
67+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
68+
if err != nil {
69+
Fail(fmt.Sprintf("Failed to get current context: %v", err))
70+
}
71+
if currentCtx != kubectx {
72+
Fail(fmt.Sprintf("Context mismatch: KUBE_CONTEXT=%s but current context is %s\n"+
73+
"Run: kubectl config use-context %s", kubectx, currentCtx, kubectx))
74+
}
75+
}
76+
5777
By("building the manager image")
5878
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
5979
_, err := utils.Run(cmd)
@@ -116,3 +136,13 @@ func teardownCertManager() {
116136
By("uninstalling CertManager")
117137
utils.UninstallCertManager()
118138
}
139+
140+
// getCurrentContext returns the current kubectl context name
141+
func getCurrentContext() (string, error) {
142+
cmd := exec.Command("kubectl", "config", "current-context")
143+
output, err := cmd.Output()
144+
if err != nil {
145+
return "", err
146+
}
147+
return strings.TrimSpace(string(output)), nil
148+
}

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

Lines changed: 31 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,33 @@ 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:
46+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
47+
// - KUBE_CONTEXT=<context-name>: (Optional) Lock to specific kubectl context
4548
func TestE2E(t *testing.T) {
4649
RegisterFailHandler(Fail)
4750
_, _ = fmt.Fprintf(GinkgoWriter, "Starting project e2e test suite\n")
4851
RunSpecs(t, "e2e suite")
4952
}
5053

5154
var _ = BeforeSuite(func() {
55+
// Display current kubectl context
56+
currentCtx, err := getCurrentContext()
57+
if err == nil {
58+
_, _ = fmt.Fprintf(GinkgoWriter, "Using kubectl context: %s\n", currentCtx)
59+
}
60+
61+
// Validate context if KUBE_CONTEXT env var is set
62+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
63+
if err != nil {
64+
Fail(fmt.Sprintf("Failed to get current context: %v", err))
65+
}
66+
if currentCtx != kubectx {
67+
Fail(fmt.Sprintf("Context mismatch: KUBE_CONTEXT=%s but current context is %s\n"+
68+
"Run: kubectl config use-context %s", kubectx, currentCtx, kubectx))
69+
}
70+
}
71+
5272
By("building the manager image")
5373
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
5474
_, err := utils.Run(cmd)
@@ -96,3 +116,13 @@ func teardownCertManager() {
96116
By("uninstalling CertManager")
97117
utils.UninstallCertManager()
98118
}
119+
120+
// getCurrentContext returns the current kubectl context name
121+
func getCurrentContext() (string, error) {
122+
cmd := exec.Command("kubectl", "config", "current-context")
123+
output, err := cmd.Output()
124+
if err != nil {
125+
return "", err
126+
}
127+
return strings.TrimSpace(string(output)), nil
128+
}

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

Lines changed: 31 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,9 @@ 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:
48+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
49+
// - KUBE_CONTEXT=<context-name>: (Optional) Lock to specific kubectl context
4750
func TestE2E(t *testing.T) {
4851
RegisterFailHandler(Fail)
4952
_, _ = fmt.Fprintf(GinkgoWriter, "Starting project e2e test suite\n")
@@ -54,6 +57,23 @@ var _ = BeforeSuite(func() {
5457
By("Ensure that Prometheus is enabled")
5558
_ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#")
5659

60+
// Display current kubectl context
61+
currentCtx, err := getCurrentContext()
62+
if err == nil {
63+
_, _ = fmt.Fprintf(GinkgoWriter, "Using kubectl context: %s\n", currentCtx)
64+
}
65+
66+
// Validate context if KUBE_CONTEXT env var is set
67+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
68+
if err != nil {
69+
Fail(fmt.Sprintf("Failed to get current context: %v", err))
70+
}
71+
if currentCtx != kubectx {
72+
Fail(fmt.Sprintf("Context mismatch: KUBE_CONTEXT=%s but current context is %s\n"+
73+
"Run: kubectl config use-context %s", kubectx, currentCtx, kubectx))
74+
}
75+
}
76+
5777
By("building the manager image")
5878
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
5979
_, err := utils.Run(cmd)
@@ -116,3 +136,13 @@ func teardownCertManager() {
116136
By("uninstalling CertManager")
117137
utils.UninstallCertManager()
118138
}
139+
140+
// getCurrentContext returns the current kubectl context name
141+
func getCurrentContext() (string, error) {
142+
cmd := exec.Command("kubectl", "config", "current-context")
143+
output, err := cmd.Output()
144+
if err != nil {
145+
return "", err
146+
}
147+
return strings.TrimSpace(string(output)), nil
148+
}

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

Lines changed: 31 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,33 @@ 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:
74+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
75+
// - KUBE_CONTEXT=<context-name>: (Optional) Lock to specific kubectl context
7376
func TestE2E(t *testing.T) {
7477
RegisterFailHandler(Fail)
7578
_, _ = fmt.Fprintf(GinkgoWriter, "Starting {{ .ProjectName }} e2e test suite\n")
7679
RunSpecs(t, "e2e suite")
7780
}
7881
7982
var _ = BeforeSuite(func() {
83+
// Display current kubectl context
84+
currentCtx, err := getCurrentContext()
85+
if err == nil {
86+
_, _ = fmt.Fprintf(GinkgoWriter, "Using kubectl context: %s\n", currentCtx)
87+
}
88+
89+
// Validate context if KUBE_CONTEXT env var is set
90+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
91+
if err != nil {
92+
Fail(fmt.Sprintf("Failed to get current context: %v", err))
93+
}
94+
if currentCtx != kubectx {
95+
Fail(fmt.Sprintf("Context mismatch: KUBE_CONTEXT=%s but current context is %s\n"+
96+
"Run: kubectl config use-context %s", kubectx, currentCtx, kubectx))
97+
}
98+
}
99+
80100
By("building the manager image")
81101
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
82102
_, err := utils.Run(cmd)
@@ -124,4 +144,14 @@ func teardownCertManager() {
124144
By("uninstalling CertManager")
125145
utils.UninstallCertManager()
126146
}
147+
148+
// getCurrentContext returns the current kubectl context name
149+
func getCurrentContext() (string, error) {
150+
cmd := exec.Command("kubectl", "config", "current-context")
151+
output, err := cmd.Output()
152+
if err != nil {
153+
return "", err
154+
}
155+
return strings.TrimSpace(string(output)), nil
156+
}
127157
`

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 // Optional kubectl context to use (empty = use default)
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
}

test/e2e/utils/test_context.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ func NewTestContext(binaryName string, env ...string) (*TestContext, error) {
7676
Namespace: fmt.Sprintf("e2e-%s-system", testSuffix),
7777
ServiceAccount: fmt.Sprintf("e2e-%s-controller-manager", testSuffix),
7878
CmdContext: cc,
79+
// Optional context lock from env var
80+
KubeContext: os.Getenv("KUBE_CONTEXT"),
7981
}
8082

8183
// For test outside of cluster we do not need to have kubectl

testdata/project-v4-multigroup/test/e2e/e2e_suite_test.go

Lines changed: 31 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,33 @@ 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:
46+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
47+
// - KUBE_CONTEXT=<context-name>: (Optional) Lock to specific kubectl context
4548
func TestE2E(t *testing.T) {
4649
RegisterFailHandler(Fail)
4750
_, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-multigroup e2e test suite\n")
4851
RunSpecs(t, "e2e suite")
4952
}
5053

5154
var _ = BeforeSuite(func() {
55+
// Display current kubectl context
56+
currentCtx, err := getCurrentContext()
57+
if err == nil {
58+
_, _ = fmt.Fprintf(GinkgoWriter, "Using kubectl context: %s\n", currentCtx)
59+
}
60+
61+
// Validate context if KUBE_CONTEXT env var is set
62+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
63+
if err != nil {
64+
Fail(fmt.Sprintf("Failed to get current context: %v", err))
65+
}
66+
if currentCtx != kubectx {
67+
Fail(fmt.Sprintf("Context mismatch: KUBE_CONTEXT=%s but current context is %s\n"+
68+
"Run: kubectl config use-context %s", kubectx, currentCtx, kubectx))
69+
}
70+
}
71+
5272
By("building the manager image")
5373
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
5474
_, err := utils.Run(cmd)
@@ -96,3 +116,13 @@ func teardownCertManager() {
96116
By("uninstalling CertManager")
97117
utils.UninstallCertManager()
98118
}
119+
120+
// getCurrentContext returns the current kubectl context name
121+
func getCurrentContext() (string, error) {
122+
cmd := exec.Command("kubectl", "config", "current-context")
123+
output, err := cmd.Output()
124+
if err != nil {
125+
return "", err
126+
}
127+
return strings.TrimSpace(string(output)), nil
128+
}

testdata/project-v4-with-plugins/test/e2e/e2e_suite_test.go

Lines changed: 31 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,33 @@ 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:
46+
// - CERT_MANAGER_INSTALL_SKIP=true: Skip CertManager installation
47+
// - KUBE_CONTEXT=<context-name>: (Optional) Lock to specific kubectl context
4548
func TestE2E(t *testing.T) {
4649
RegisterFailHandler(Fail)
4750
_, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-with-plugins e2e test suite\n")
4851
RunSpecs(t, "e2e suite")
4952
}
5053

5154
var _ = BeforeSuite(func() {
55+
// Display current kubectl context
56+
currentCtx, err := getCurrentContext()
57+
if err == nil {
58+
_, _ = fmt.Fprintf(GinkgoWriter, "Using kubectl context: %s\n", currentCtx)
59+
}
60+
61+
// Validate context if KUBE_CONTEXT env var is set
62+
if kubectx := os.Getenv("KUBE_CONTEXT"); kubectx != "" {
63+
if err != nil {
64+
Fail(fmt.Sprintf("Failed to get current context: %v", err))
65+
}
66+
if currentCtx != kubectx {
67+
Fail(fmt.Sprintf("Context mismatch: KUBE_CONTEXT=%s but current context is %s\n"+
68+
"Run: kubectl config use-context %s", kubectx, currentCtx, kubectx))
69+
}
70+
}
71+
5272
By("building the manager image")
5373
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", managerImage))
5474
_, err := utils.Run(cmd)
@@ -96,3 +116,13 @@ func teardownCertManager() {
96116
By("uninstalling CertManager")
97117
utils.UninstallCertManager()
98118
}
119+
120+
// getCurrentContext returns the current kubectl context name
121+
func getCurrentContext() (string, error) {
122+
cmd := exec.Command("kubectl", "config", "current-context")
123+
output, err := cmd.Output()
124+
if err != nil {
125+
return "", err
126+
}
127+
return strings.TrimSpace(string(output)), nil
128+
}

0 commit comments

Comments
 (0)