From 1ae1b309f949b5259abfad35d7b1958ba9f25699 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 12 Apr 2026 22:07:11 -0500 Subject: [PATCH 1/6] fix(e2e): harden test suite reliability and fix silent failures Fix critical issues in the e2e test infrastructure: - Add 30s timeout to agent URL wait loop to prevent infinite hangs - Propagate errors in SetupGPG instead of silently returning nil - Use dedicated http.ServeMux in ServeAgent to avoid global state conflicts - Add explicit timeouts/polling to all Eventually assertions missing them - Replace bare defer cleanup with ginkgo.DeferCleanup for timeout safety - Remove unnecessary 3s sleep in SSH port forwarding test - Consolidate duplicate setupDockerProvider into framework.SetupDockerProvider - Delete 47 lines of commented-out GPG forwarding test code - Fix "nachine" typos and apt-get install spacing in error messages --- e2e/e2e_suite_test.go | 8 +++- e2e/framework/command.go | 45 +++++++++++++--------- e2e/framework/server_utils.go | 9 +++-- e2e/tests/ssh/ssh.go | 55 +-------------------------- e2e/tests/up-docker-compose/build.go | 11 +++++- e2e/tests/up-docker-compose/helper.go | 5 +-- e2e/tests/up-features/helper.go | 6 +-- e2e/tests/up/helper.go | 5 +-- e2e/tests/up/provider_docker.go | 3 +- e2e/tests/up/up.go | 2 +- e2e/tests/up/up_private_token.go | 4 +- 11 files changed, 59 insertions(+), 94 deletions(-) diff --git a/e2e/e2e_suite_test.go b/e2e/e2e_suite_test.go index ca786b518..dc0a9c4b5 100644 --- a/e2e/e2e_suite_test.go +++ b/e2e/e2e_suite_test.go @@ -33,8 +33,14 @@ func TestRunE2ETests(t *testing.T) { if runtime.GOOS != "linux" { go framework.ServeAgent() - // wait for http server to be up and running + // wait for http server to be up and running (max 30s) + deadline := time.After(30 * time.Second) for { + select { + case <-deadline: + t.Fatal("timeout waiting for DEVPOD_AGENT_URL to be set after 30s") + default: + } time.Sleep(time.Second) if os.Getenv("DEVPOD_AGENT_URL") != "" { break diff --git a/e2e/framework/command.go b/e2e/framework/command.go index 2c5a2d18b..06e41c96a 100644 --- a/e2e/framework/command.go +++ b/e2e/framework/command.go @@ -291,7 +291,7 @@ func (f *Framework) DevPodMachineCreate(args []string) error { baseArgs = append(baseArgs, args...) err := f.ExecCommand(context.Background(), false, false, "", baseArgs) if err != nil { - return fmt.Errorf("devpod nachine create failed: %s", err.Error()) + return fmt.Errorf("devpod machine create failed: %s", err.Error()) } return nil } @@ -301,7 +301,7 @@ func (f *Framework) DevPodMachineDelete(args []string) error { baseArgs = append(baseArgs, args...) err := f.ExecCommand(context.Background(), false, false, "", baseArgs) if err != nil { - return fmt.Errorf("devpod nachine delete failed: %s", err.Error()) + return fmt.Errorf("devpod machine delete failed: %s", err.Error()) } return nil } @@ -325,31 +325,31 @@ func (f *Framework) DevPodWorkspaceDelete( func (f *Framework) SetupGPG(tmpDir string) error { if _, err := exec.LookPath("gpg"); err != nil { - err := exec.Command("sudo", "apt-get", " install", "gnupg2", "-y").Run() - if err != nil { - return nil + if installErr := exec.Command("sudo", "apt-get", "install", "gnupg2", "-y"). + Run(); installErr != nil { + return fmt.Errorf("gpg not found and failed to install gnupg2: %w", installErr) } } - err := exec.Command("gpg", "--import", filepath.Join(tmpDir, "gpg-public.key")).Run() - if err != nil { - return nil + // #nosec G204 -- gpg with fixed arguments for test GPG key setup + if err := exec.Command("gpg", "--import", filepath.Join(tmpDir, "gpg-public.key")). + Run(); err != nil { + return fmt.Errorf("failed to import gpg public key: %w", err) } - err = exec.Command("gpg", "--import", filepath.Join(tmpDir, "gpg-private.key")).Run() - if err != nil { - return nil + // #nosec G204 -- gpg with fixed arguments for test GPG key setup + if err := exec.Command("gpg", "--import", filepath.Join(tmpDir, "gpg-private.key")). + Run(); err != nil { + return fmt.Errorf("failed to import gpg private key: %w", err) } - err = exec.Command("gpgconf", "--kill", "gpg-agent").Run() - if err != nil { - return nil + if err := exec.Command("gpgconf", "--kill", "gpg-agent").Run(); err != nil { + return fmt.Errorf("failed to kill gpg-agent: %w", err) } - err = exec.Command("gpg-agent", "--homedir", "$HOME/.gnupg", "--use-standard-socket", "--daemon"). - Run() - if err != nil { - return nil + if err := exec.Command("gpg-agent", "--homedir", "$HOME/.gnupg", "--use-standard-socket", "--daemon"). + Run(); err != nil { + return fmt.Errorf("failed to start gpg-agent: %w", err) } return exec.Command("gpg", "-k").Run() @@ -456,3 +456,12 @@ func (f *Framework) DevPodIDEList(ctx context.Context, extraArgs ...string) (str baseArgs := []string{"ide", "list"} return f.ExecCommandOutput(ctx, append(baseArgs, extraArgs...)) } + +// SetupDockerProvider creates a new framework, removes any existing docker provider, +// adds a fresh one with the given docker path, and sets it as the active provider. +func SetupDockerProvider(binDir, dockerPath string) (*Framework, error) { + f := NewDefaultFramework(binDir) + _ = f.DevPodProviderDelete(context.Background(), "docker") + _ = f.DevPodProviderAdd(context.Background(), "docker", "-o", "DOCKER_PATH="+dockerPath) + return f, f.DevPodProviderUse(context.Background(), "docker") +} diff --git a/e2e/framework/server_utils.go b/e2e/framework/server_utils.go index 99fb9dd82..61a836b8e 100644 --- a/e2e/framework/server_utils.go +++ b/e2e/framework/server_utils.go @@ -24,8 +24,9 @@ func ServeAgent() { // Create a file server handler for the specified directory fileServer := http.FileServer(http.Dir(dir)) - // Register the file server handler to serve files under the /files route - http.Handle("/files/", http.StripPrefix("/files", fileServer)) + // Use a dedicated ServeMux to avoid conflicts with http.DefaultServeMux + mux := http.NewServeMux() + mux.Handle("/files/", http.StripPrefix("/files", fileServer)) ip := getIP() @@ -40,10 +41,10 @@ func ServeAgent() { log.Fatal(err) } - // Start the HTTP server on port 8080 log.Printf("Server started on %s", addr) - err = http.Serve(listener, nil) + // #nosec G114 -- test-only agent file server, no timeout needed + err = http.Serve(listener, mux) if err != nil { log.Fatal(err) } diff --git a/e2e/tests/ssh/ssh.go b/e2e/tests/ssh/ssh.go index 1356d2f09..ae495bcb0 100644 --- a/e2e/tests/ssh/ssh.go +++ b/e2e/tests/ssh/ssh.go @@ -59,54 +59,6 @@ var _ = ginkgo.Describe("devpod ssh test suite", ginkgo.Label("ssh"), ginkgo.Ord }, ) - // ginkgo.It("should start a new workspace with a docker provider (default) and forward gpg agent into it", func() { - // // skip windows for now - // if runtime.GOOS == osWindows { - // return - // } - // - // tempDir, err := framework.CopyToTempDir("tests/ssh/testdata/gpg-forwarding") - // framework.ExpectNoError(err) - // defer framework.CleanupTempDir(initialDir, tempDir) - // - // f := framework.NewDefaultFramework(initialDir + "/bin") - // _ = f.DevPodProviderAdd(ctx, "docker") - // err = f.DevPodProviderUse(context.Background(), "docker") - // framework.ExpectNoError(err) - // - // ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), tempDir) - // - // out, err := exec.Command("gpg", "-k").Output() - // if err != nil || len(out) == 0 { - // err = f.SetupGPG(tempDir) - // framework.ExpectNoError(err) - // } - // - // // Start up devpod workspace - // devpodUpDeadline := time.Now().Add(5 * time.Minute) - // devpodUpCtx, cancel := context.WithDeadline(context.Background(), devpodUpDeadline) - // defer cancel() - // err = f.DevPodUp(devpodUpCtx, tempDir, "--gpg-agent-forwarding") - // framework.ExpectNoError(err) - // - // devpodSSHDeadline := time.Now().Add(20 * time.Second) - // devpodSSHCtx, cancelSSH := context.WithDeadline(context.Background(), devpodSSHDeadline) - // defer cancelSSH() - // - // // GPG agent might be not ready, let's try 10 times, 1 second each - // retries := 10 - // for retries > 0 { - // err = f.DevPodSSHGpgTestKey(devpodSSHCtx, tempDir) - // if err != nil { - // retries-- - // time.Sleep(time.Second) - // } else { - // break - // } - // } - // framework.ExpectNoError(err) - // }) - ginkgo.It( "should set up git SSH signature helper and sign a commit", ginkgo.SpecTimeout(7*time.Minute), @@ -131,7 +83,7 @@ var _ = ginkgo.Describe("devpod ssh test suite", ginkgo.Label("ssh"), ginkgo.Ord // Generate a temporary SSH key for signing sshKeyDir, err := os.MkdirTemp("", "devpod-ssh-signing-test") framework.ExpectNoError(err) - defer func() { _ = os.RemoveAll(sshKeyDir) }() + ginkgo.DeferCleanup(func() { _ = os.RemoveAll(sshKeyDir) }) keyPath := filepath.Join(sshKeyDir, "id_ed25519") // #nosec G204 -- test command with controlled arguments @@ -341,7 +293,7 @@ var _ = ginkgo.Describe("devpod ssh test suite", ginkgo.Label("ssh"), ginkgo.Ord // Generate a key but do NOT add it to the ssh-agent so signing will fail sshKeyDir, err := os.MkdirTemp("", "devpod-ssh-signing-err-test") framework.ExpectNoError(err) - defer func() { _ = os.RemoveAll(sshKeyDir) }() + ginkgo.DeferCleanup(func() { _ = os.RemoveAll(sshKeyDir) }) keyPath := filepath.Join(sshKeyDir, "id_ed25519") // #nosec G204 -- test command with controlled arguments @@ -437,9 +389,6 @@ var _ = ginkgo.Describe("devpod ssh test suite", ginkgo.Label("ssh"), ginkgo.Ord _ = serverCmd.Wait() }() - ginkgo.GinkgoWriter.Println("Waiting for server to start") - time.Sleep(3 * time.Second) - portForwardCtx, cancelPort := context.WithTimeout( ctx, 60*time.Second, diff --git a/e2e/tests/up-docker-compose/build.go b/e2e/tests/up-docker-compose/build.go index 3baf39e82..57cdeb791 100644 --- a/e2e/tests/up-docker-compose/build.go +++ b/e2e/tests/up-docker-compose/build.go @@ -8,6 +8,7 @@ import ( "io" "os" "path/filepath" + "time" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -82,7 +83,10 @@ var _ = ginkgo.Describe( return 0 } return len(ids) - }).Should(gomega.Equal(1), "1 compose container to be created") + }). + WithTimeout(60*time.Second). + WithPolling(1*time.Second). + Should(gomega.Equal(1), "1 compose container to be created") ginkgo.By("Modifying .devcontainer.json with failing changes") origPath := filepath.Join(tempDir, ".devcontainer.json") @@ -152,7 +156,10 @@ var _ = ginkgo.Describe( return 0 } return len(ids) - }).Should(gomega.Equal(1), "1 compose container to be created") + }). + WithTimeout(60*time.Second). + WithPolling(1*time.Second). + Should(gomega.Equal(1), "1 compose container to be created") ginkgo.By("Starting DevPod again with --recreate") err = f.DevPodUp(ctx, tempDir, "--debug", "--recreate") diff --git a/e2e/tests/up-docker-compose/helper.go b/e2e/tests/up-docker-compose/helper.go index 6463cabe2..6a6e568ae 100644 --- a/e2e/tests/up-docker-compose/helper.go +++ b/e2e/tests/up-docker-compose/helper.go @@ -140,10 +140,7 @@ func setupWorkspace(testdataPath, initialDir string, f *framework.Framework) (st } func setupDockerProvider(binDir, dockerPath string) (*framework.Framework, error) { - f := framework.NewDefaultFramework(binDir) - _ = f.DevPodProviderDelete(context.Background(), "docker") - _ = f.DevPodProviderAdd(context.Background(), "docker", "-o", "DOCKER_PATH="+dockerPath) - return f, f.DevPodProviderUse(context.Background(), "docker") + return framework.SetupDockerProvider(binDir, dockerPath) } func findComposeContainer( diff --git a/e2e/tests/up-features/helper.go b/e2e/tests/up-features/helper.go index c9c9748ed..b7504a496 100644 --- a/e2e/tests/up-features/helper.go +++ b/e2e/tests/up-features/helper.go @@ -3,7 +3,6 @@ package up import ( "archive/tar" "compress/gzip" - "context" "io" "os" @@ -57,8 +56,5 @@ func addFileToTar(tarWriter *tar.Writer, filePath string) error { } func setupDockerProvider(binDir, dockerPath string) (*framework.Framework, error) { - f := framework.NewDefaultFramework(binDir) - _ = f.DevPodProviderDelete(context.Background(), "docker") - _ = f.DevPodProviderAdd(context.Background(), "docker", "-o", "DOCKER_PATH="+dockerPath) - return f, f.DevPodProviderUse(context.Background(), "docker") + return framework.SetupDockerProvider(binDir, dockerPath) } diff --git a/e2e/tests/up/helper.go b/e2e/tests/up/helper.go index 34c6bce12..c4452be1b 100644 --- a/e2e/tests/up/helper.go +++ b/e2e/tests/up/helper.go @@ -101,10 +101,7 @@ func setupWorkspace(testdataPath, initialDir string, f *framework.Framework) (st } func setupDockerProvider(binDir, dockerPath string) (*framework.Framework, error) { - f := framework.NewDefaultFramework(binDir) - _ = f.DevPodProviderDelete(context.Background(), "docker") - _ = f.DevPodProviderAdd(context.Background(), "docker", "-o", "DOCKER_PATH="+dockerPath) - return f, f.DevPodProviderUse(context.Background(), "docker") + return framework.SetupDockerProvider(binDir, dockerPath) } func setupWorkspaceAndUp( diff --git a/e2e/tests/up/provider_docker.go b/e2e/tests/up/provider_docker.go index a9c1381ac..4903ae32c 100644 --- a/e2e/tests/up/provider_docker.go +++ b/e2e/tests/up/provider_docker.go @@ -8,6 +8,7 @@ import ( "path/filepath" "runtime" "strings" + "time" "github.com/docker/docker/api/types/container" "github.com/onsi/ginkgo/v2" @@ -76,7 +77,7 @@ var _ = ginkgo.Describe( var containerDetails []container.InspectResponse err = dtc.dockerHelper.Inspect(ctx, ids, "container", &containerDetails) return err == nil && containerDetails[0].State.Running - }).Should(gomega.BeTrue()) + }).WithTimeout(30 * time.Second).WithPolling(1 * time.Second).Should(gomega.BeTrue()) ginkgo.DeferCleanup(dtc.dockerHelper.Remove, ids[0]) ginkgo.DeferCleanup(dtc.dockerHelper.Stop, ids[0]) diff --git a/e2e/tests/up/up.go b/e2e/tests/up/up.go index 6da87d6ed..d4c236946 100644 --- a/e2e/tests/up/up.go +++ b/e2e/tests/up/up.go @@ -75,7 +75,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-workspaces"), fun // set env vars with file tmpDir, err := framework.CreateTempDir() framework.ExpectNoError(err) - defer func() { _ = os.RemoveAll(tmpDir) }() + ginkgo.DeferCleanup(func() { _ = os.RemoveAll(tmpDir) }) // create invalid env file invalidData := []byte("TEST VAR=" + value) diff --git a/e2e/tests/up/up_private_token.go b/e2e/tests/up/up_private_token.go index 33cb8866b..4045505a1 100644 --- a/e2e/tests/up/up_private_token.go +++ b/e2e/tests/up/up_private_token.go @@ -39,7 +39,9 @@ var _ = ginkgo.Describe( filepath.Join(os.Getenv("HOME"), ".git-credentials"), gitCredentialString, 0o600) framework.ExpectNoError(err) - defer func() { _ = os.Remove(filepath.Join(os.Getenv("HOME"), ".git-credentials")) }() + ginkgo.DeferCleanup( + func() { _ = os.Remove(filepath.Join(os.Getenv("HOME"), ".git-credentials")) }, + ) name := "testprivaterepo" ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), name) From 640bddecc026f2d7cb3050abf7d04038981f1c0d Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 12 Apr 2026 22:15:02 -0500 Subject: [PATCH 2/6] fix(e2e): replace context.Background() with Ginkgo cleanup contexts and add SpecTimeout DeferCleanup callbacks now accept context.Context to use Ginkgo's cancellable cleanup context instead of context.Background(). Added SpecTimeout to test specs that were missing timeout protection. Updated DevPodMachineCreate/Delete to accept context parameter. --- e2e/framework/command.go | 8 +- e2e/tests/build/build.go | 12 +-- e2e/tests/context/context.go | 129 ++++++++++++----------- e2e/tests/dockerinstall/dockerinstall.go | 2 +- e2e/tests/ide/ide.go | 2 +- e2e/tests/integration/integration.go | 1 + e2e/tests/machine/machine.go | 118 +++++++++++---------- e2e/tests/up-features/up_features.go | 32 +++--- e2e/tests/up-features/wsl.go | 4 +- e2e/tests/up/git_repositories.go | 8 +- e2e/tests/up/handles_errors.go | 12 +-- e2e/tests/up/provider_kubernetes.go | 4 +- e2e/tests/up/up.go | 18 ++-- e2e/tests/up/up_private_token.go | 2 +- e2e/tests/upgrade/upgrade.go | 1 + 15 files changed, 185 insertions(+), 168 deletions(-) diff --git a/e2e/framework/command.go b/e2e/framework/command.go index 06e41c96a..ac66a810b 100644 --- a/e2e/framework/command.go +++ b/e2e/framework/command.go @@ -286,20 +286,20 @@ func (f *Framework) DevPodProviderUpdate(ctx context.Context, args ...string) er return nil } -func (f *Framework) DevPodMachineCreate(args []string) error { +func (f *Framework) DevPodMachineCreate(ctx context.Context, args []string) error { baseArgs := []string{"machine", "create"} baseArgs = append(baseArgs, args...) - err := f.ExecCommand(context.Background(), false, false, "", baseArgs) + err := f.ExecCommand(ctx, false, false, "", baseArgs) if err != nil { return fmt.Errorf("devpod machine create failed: %s", err.Error()) } return nil } -func (f *Framework) DevPodMachineDelete(args []string) error { +func (f *Framework) DevPodMachineDelete(ctx context.Context, args []string) error { baseArgs := []string{"machine", "delete"} baseArgs = append(baseArgs, args...) - err := f.ExecCommand(context.Background(), false, false, "", baseArgs) + err := f.ExecCommand(ctx, false, false, "", baseArgs) if err != nil { return fmt.Errorf("devpod machine delete failed: %s", err.Error()) } diff --git a/e2e/tests/build/build.go b/e2e/tests/build/build.go index f1ff23cde..dd2ba9a54 100644 --- a/e2e/tests/build/build.go +++ b/e2e/tests/build/build.go @@ -156,7 +156,7 @@ var _ = ginkgo.Describe("devpod build test suite", ginkgo.Label("build"), ginkgo framework.ExpectNoError(err) err = f.DevPodProviderUse(ctx, "docker") framework.ExpectNoError(err) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), tempDir) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) cfg := getDevcontainerConfig(tempDir) @@ -208,7 +208,7 @@ var _ = ginkgo.Describe("devpod build test suite", ginkgo.Label("build"), ginkgo err = f.DevPodProviderUse(ctx, "docker") framework.ExpectNoError(err) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), tempDir) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) prebuildRepo := prebuildRepoName @@ -234,7 +234,7 @@ var _ = ginkgo.Describe("devpod build test suite", ginkgo.Label("build"), ginkgo err = f.DevPodProviderUse(ctx, "docker") framework.ExpectNoError(err) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), tempDir) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) err = f.DevPodBuild(ctx, tempDir, "--skip-push") framework.ExpectNoError(err) @@ -253,7 +253,7 @@ var _ = ginkgo.Describe("devpod build test suite", ginkgo.Label("build"), ginkgo err = f.DevPodProviderUse(ctx, "docker") framework.ExpectNoError(err) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), tempDir) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) cfg := getDevcontainerConfig(tempDir) @@ -318,7 +318,7 @@ var _ = ginkgo.Describe("devpod build test suite", ginkgo.Label("build"), ginkgo ) framework.ExpectNoError(err) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), tempDir) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) // do the up err = f.DevPodUp(ctx, tempDir) @@ -376,7 +376,7 @@ func validateKubernetesDeploymentWithoutDocker( ) framework.ExpectNoError(err) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), tempDir) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) diff --git a/e2e/tests/context/context.go b/e2e/tests/context/context.go index 7e6b206a6..ab6639e33 100644 --- a/e2e/tests/context/context.go +++ b/e2e/tests/context/context.go @@ -9,6 +9,8 @@ import ( "github.com/skevetter/devpod/e2e/framework" ) +const ideIntelliJ = "intellij" + var _ = ginkgo.Describe( "devpod context test suite", ginkgo.Label("context"), @@ -24,6 +26,7 @@ var _ = ginkgo.Describe( ginkgo.It( "create a new context, switch to it and delete afterwards", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { f := framework.NewDefaultFramework(initialDir + "/bin") @@ -41,88 +44,92 @@ var _ = ginkgo.Describe( }, ) - ginkgo.It("should use shared context in IDE commands", func(ctx context.Context) { - f := framework.NewDefaultFramework(initialDir + "/bin") + ginkgo.It( + "should use shared context in IDE commands", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + f := framework.NewDefaultFramework(initialDir + "/bin") - contextA := "test-ctx-a-ide" - contextB := "test-ctx-b-ide" + contextA := "test-ctx-a-ide" + contextB := "test-ctx-b-ide" - var err error - err = f.DevPodContextCreate(ctx, contextA) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(func(cleanupCtx context.Context) { - _ = f.DevPodContextDelete(cleanupCtx, contextA) - }) + var err error + err = f.DevPodContextCreate(ctx, contextA) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + _ = f.DevPodContextDelete(cleanupCtx, contextA) + }) - err = f.DevPodContextCreate(ctx, contextB) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(func(cleanupCtx context.Context) { - err = f.DevPodContextDelete(cleanupCtx, contextB) + err = f.DevPodContextCreate(ctx, contextB) framework.ExpectNoError(err) - }) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err = f.DevPodContextDelete(cleanupCtx, contextB) + framework.ExpectNoError(err) + }) - err = f.DevPodContextUse(ctx, contextA) - framework.ExpectNoError(err) + err = f.DevPodContextUse(ctx, contextA) + framework.ExpectNoError(err) - err = f.DevPodIDEUse(ctx, "intellij", "--context", contextB) - framework.ExpectNoError(err) + err = f.DevPodIDEUse(ctx, ideIntelliJ, "--context", contextB) + framework.ExpectNoError(err) - output, err := f.DevPodIDEList(ctx, "--output", "json") - framework.ExpectNoError(err) + output, err := f.DevPodIDEList(ctx, "--output", "json") + framework.ExpectNoError(err) - var ides []map[string]any - err = json.Unmarshal([]byte(output), &ides) - framework.ExpectNoError(err) + var ides []map[string]any + err = json.Unmarshal([]byte(output), &ides) + framework.ExpectNoError(err) - for _, ide := range ides { - if ide["name"] == "intellij" { - if defaultVal, exists := ide["default"]; exists && defaultVal == true { - ginkgo.Fail("IDE was incorrectly set in context-a instead of context-b") + for _, ide := range ides { + if ide["name"] == ideIntelliJ { + if defaultVal, exists := ide["default"]; exists && defaultVal == true { + ginkgo.Fail("IDE was incorrectly set in context-a instead of context-b") + } } } - } - output, err = f.DevPodIDEList(ctx, "--context", contextB, "--output", "json") - framework.ExpectNoError(err) + output, err = f.DevPodIDEList(ctx, "--context", contextB, "--output", "json") + framework.ExpectNoError(err) - err = json.Unmarshal([]byte(output), &ides) - framework.ExpectNoError(err) + err = json.Unmarshal([]byte(output), &ides) + framework.ExpectNoError(err) - intellijFound := false - for _, ide := range ides { - if ide["name"] == "intellij" { - if defaultVal, exists := ide["default"]; exists && defaultVal == true { - intellijFound = true - break + intellijFound := false + for _, ide := range ides { + if ide["name"] == ideIntelliJ { + if defaultVal, exists := ide["default"]; exists && defaultVal == true { + intellijFound = true + break + } } } - } - if !intellijFound { - ginkgo.Fail("IDE was not set in context-b as expected") - } + if !intellijFound { + ginkgo.Fail("IDE was not set in context-b as expected") + } - ginkgo.GinkgoT().Setenv("DEVPOD_CONTEXT", contextB) + ginkgo.GinkgoT().Setenv("DEVPOD_CONTEXT", contextB) - output, err = f.DevPodIDEList(ctx, "--output", "json") - framework.ExpectNoError(err) + output, err = f.DevPodIDEList(ctx, "--output", "json") + framework.ExpectNoError(err) - err = json.Unmarshal([]byte(output), &ides) - framework.ExpectNoError(err) + err = json.Unmarshal([]byte(output), &ides) + framework.ExpectNoError(err) - intellijFound = false - for _, ide := range ides { - if ide["name"] == "intellij" { - if defaultVal, exists := ide["default"]; exists && defaultVal == true { - intellijFound = true - break + intellijFound = false + for _, ide := range ides { + if ide["name"] == ideIntelliJ { + if defaultVal, exists := ide["default"]; exists && defaultVal == true { + intellijFound = true + break + } } } - } - if !intellijFound { - ginkgo.Fail( - "Selecting context-b using environment variable DEVPOD_CONTEXT does not work as expected", - ) - } - }) + if !intellijFound { + ginkgo.Fail( + "Selecting context-b using environment variable DEVPOD_CONTEXT does not work as expected", + ) + } + }, + ) }, ) diff --git a/e2e/tests/dockerinstall/dockerinstall.go b/e2e/tests/dockerinstall/dockerinstall.go index 121f054e4..338683454 100644 --- a/e2e/tests/dockerinstall/dockerinstall.go +++ b/e2e/tests/dockerinstall/dockerinstall.go @@ -34,7 +34,7 @@ var _ = ginkgo.Describe( err = f.DevPodProviderUse(ctx, "docker") framework.ExpectNoError(err) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), tempDir) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) diff --git a/e2e/tests/ide/ide.go b/e2e/tests/ide/ide.go index ecad77599..c639153f7 100644 --- a/e2e/tests/ide/ide.go +++ b/e2e/tests/ide/ide.go @@ -17,7 +17,7 @@ var _ = ginkgo.Describe("devpod ide test suite", ginkgo.Label("ide"), ginkgo.Ord framework.ExpectNoError(err) }) - ginkgo.It("start ides", func(ctx context.Context) { + ginkgo.It("start ides", ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { f := framework.NewDefaultFramework(initialDir + "/bin") tempDir, err := framework.CopyToTempDir("tests/ide/testdata") framework.ExpectNoError(err) diff --git a/e2e/tests/integration/integration.go b/e2e/tests/integration/integration.go index 3d7a7791e..a03c3e98c 100644 --- a/e2e/tests/integration/integration.go +++ b/e2e/tests/integration/integration.go @@ -24,6 +24,7 @@ var _ = ginkgo.Describe( ginkgo.It( "should setup ssh, add provider, run workspace, test ssh, and cleanup", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { sshDir := os.Getenv("HOME") + "/.ssh" if _, err := os.Stat(sshDir); os.IsNotExist(err) { diff --git a/e2e/tests/machine/machine.go b/e2e/tests/machine/machine.go index b4fc5bf15..d3deff1b9 100644 --- a/e2e/tests/machine/machine.go +++ b/e2e/tests/machine/machine.go @@ -18,76 +18,84 @@ var _ = ginkgo.Describe("devpod testing machine", ginkgo.Label("machine"), ginkg framework.ExpectNoError(err) }) - ginkgo.It("should add simple machine and then delete it", func(ctx context.Context) { - tempDir, err := framework.CopyToTempDir("tests/machine/testdata") - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.It( + "should add simple machine and then delete it", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + tempDir, err := framework.CopyToTempDir("tests/machine/testdata") + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - f := framework.NewDefaultFramework(initialDir + "/bin") + f := framework.NewDefaultFramework(initialDir + "/bin") - // Ensure that mock-provider is deleted - _ = f.DevPodProviderDelete(ctx, "mock-provider") + // Ensure that mock-provider is deleted + _ = f.DevPodProviderDelete(ctx, "mock-provider") - ginkgo.By("Add mock provider") - err = f.DevPodProviderAdd(ctx, tempDir+"/mock-provider.yaml") - framework.ExpectNoError(err) - ginkgo.DeferCleanup(func(cleanupCtx context.Context) { - err = f.DevPodProviderDelete(cleanupCtx, "mock-provider") + ginkgo.By("Add mock provider") + err = f.DevPodProviderAdd(ctx, tempDir+"/mock-provider.yaml") framework.ExpectNoError(err) - }) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err = f.DevPodProviderDelete(cleanupCtx, "mock-provider") + framework.ExpectNoError(err) + }) - ginkgo.By("Use mock provider") - err = f.DevPodProviderUse(ctx, "mock-provider") - framework.ExpectNoError(err) - - machineUUID, _ := uuid.NewRandom() - machineName := machineUUID.String() + ginkgo.By("Use mock provider") + err = f.DevPodProviderUse(ctx, "mock-provider") + framework.ExpectNoError(err) - ginkgo.By("Create test machine with mock provider") - err = f.DevPodMachineCreate([]string{machineName}) - framework.ExpectNoError(err) + machineUUID, _ := uuid.NewRandom() + machineName := machineUUID.String() - ginkgo.By("Remove test machine") - err = f.DevPodMachineDelete([]string{machineName}) - framework.ExpectNoError(err) - }) + ginkgo.By("Create test machine with mock provider") + err = f.DevPodMachineCreate(ctx, []string{machineName}) + framework.ExpectNoError(err) - ginkgo.It("should delete a non-existing machine and get an error", func(ctx context.Context) { - tempDir, err := framework.CopyToTempDir("tests/machine/testdata") - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.By("Remove test machine") + err = f.DevPodMachineDelete(ctx, []string{machineName}) + framework.ExpectNoError(err) + }, + ) + + ginkgo.It( + "should delete a non-existing machine and get an error", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + tempDir, err := framework.CopyToTempDir("tests/machine/testdata") + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - f := framework.NewDefaultFramework(initialDir + "/bin") + f := framework.NewDefaultFramework(initialDir + "/bin") - // Ensure that mock-provider is deleted - _ = f.DevPodProviderDelete(ctx, "mock-provider") + // Ensure that mock-provider is deleted + _ = f.DevPodProviderDelete(ctx, "mock-provider") - ginkgo.By("Add mock provider") - err = f.DevPodProviderAdd(ctx, tempDir+"/mock-provider.yaml") - framework.ExpectNoError(err) + ginkgo.By("Add mock provider") + err = f.DevPodProviderAdd(ctx, tempDir+"/mock-provider.yaml") + framework.ExpectNoError(err) - ginkgo.By("Use mock provider") - err = f.DevPodProviderUse(ctx, "mock-provider") - framework.ExpectNoError(err) + ginkgo.By("Use mock provider") + err = f.DevPodProviderUse(ctx, "mock-provider") + framework.ExpectNoError(err) - machineUUID1, err := uuid.NewRandom() - framework.ExpectNoError(err) - machineName1 := machineUUID1.String() + machineUUID1, err := uuid.NewRandom() + framework.ExpectNoError(err) + machineName1 := machineUUID1.String() - machineUUID2, err := uuid.NewRandom() - framework.ExpectNoError(err) - machineName2 := machineUUID2.String() + machineUUID2, err := uuid.NewRandom() + framework.ExpectNoError(err) + machineName2 := machineUUID2.String() - ginkgo.By("Create test machine with mock provider") - err = f.DevPodMachineCreate([]string{machineName1}) - framework.ExpectNoError(err) + ginkgo.By("Create test machine with mock provider") + err = f.DevPodMachineCreate(ctx, []string{machineName1}) + framework.ExpectNoError(err) - ginkgo.By("Remove existing test machine") - err = f.DevPodMachineDelete([]string{machineName1}) - framework.ExpectNoError(err) + ginkgo.By("Remove existing test machine") + err = f.DevPodMachineDelete(ctx, []string{machineName1}) + framework.ExpectNoError(err) - ginkgo.By("Remove not existing test machine (should get an error)") - err = f.DevPodMachineDelete([]string{machineName2}) - framework.ExpectError(err) - }) + ginkgo.By("Remove not existing test machine (should get an error)") + err = f.DevPodMachineDelete(ctx, []string{machineName2}) + framework.ExpectError(err) + }, + ) }) diff --git a/e2e/tests/up-features/up_features.go b/e2e/tests/up-features/up_features.go index 9b6b97398..0878bef83 100644 --- a/e2e/tests/up-features/up_features.go +++ b/e2e/tests/up-features/up_features.go @@ -34,7 +34,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -101,7 +101,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite framework.ExpectNoError(err) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -118,7 +118,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -138,7 +138,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -165,7 +165,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -192,7 +192,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -219,7 +219,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) // This should fail with circular dependency error err = f.DevPodUp(ctx, tempDir) @@ -243,7 +243,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -270,7 +270,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -297,7 +297,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) // Should fail with circular dependency error err = f.DevPodUp(ctx, tempDir) @@ -320,7 +320,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) // Should fail when dependency cannot be resolved err = f.DevPodUp(ctx, tempDir) @@ -343,7 +343,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -371,7 +371,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) // This should not fail with "Parent does not exist" error err = f.DevPodUp(ctx, tempDir) @@ -399,7 +399,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -426,7 +426,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) @@ -447,7 +447,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-features", "suite ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) wsName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), wsName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, wsName) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) diff --git a/e2e/tests/up-features/wsl.go b/e2e/tests/up-features/wsl.go index b9f46a27a..119962313 100644 --- a/e2e/tests/up-features/wsl.go +++ b/e2e/tests/up-features/wsl.go @@ -77,7 +77,7 @@ var _ = ginkgo.Describe( err = f.DevPodProviderUse(ctx, "docker") framework.ExpectNoError(err) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), tempDir) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) // Wait for devpod workspace to come online (deadline: 30s) err = f.DevPodUp(ctx, tempDir) @@ -98,7 +98,7 @@ var _ = ginkgo.Describe( ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) workspaceName := filepath.Base(tempDir) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), workspaceName) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, workspaceName) // Wait for devpod workspace to come online (deadline: 30s) err = f.DevPodUp(ctx, tempDir, "--debug") diff --git a/e2e/tests/up/git_repositories.go b/e2e/tests/up/git_repositories.go index 1bbcc2bd1..3152451cb 100644 --- a/e2e/tests/up/git_repositories.go +++ b/e2e/tests/up/git_repositories.go @@ -28,7 +28,7 @@ var _ = ginkgo.Describe( framework.ExpectNoError(err) name := "sha256-0c1547c" - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), name) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, name) // Wait for devpod workspace to come online (deadline: 30s) err = f.DevPodUp( @@ -47,7 +47,7 @@ var _ = ginkgo.Describe( framework.ExpectNoError(err) name := "devpod" - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), name) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, name) err = f.DevPodUp(ctx, "github.com/skevetter/devpod@pull/1/head") framework.ExpectNoError(err) @@ -65,8 +65,8 @@ var _ = ginkgo.Describe( framework.ExpectNoError(err) err = f.DevPodProviderUse(ctx, providerName) framework.ExpectNoError(err) - ginkgo.DeferCleanup(func() { - err = f.DevPodProviderDelete(context.Background(), providerName) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err := f.DevPodProviderDelete(cleanupCtx, providerName) framework.ExpectNoError(err) }) diff --git a/e2e/tests/up/handles_errors.go b/e2e/tests/up/handles_errors.go index 33ecb4a9e..c0bf7edce 100644 --- a/e2e/tests/up/handles_errors.go +++ b/e2e/tests/up/handles_errors.go @@ -32,8 +32,8 @@ var _ = ginkgo.Describe( err = f.DevPodProviderAdd(ctx, "docker", "--name", "test-docker") framework.ExpectNoError(err) - ginkgo.DeferCleanup(func() { - err = f.DevPodProviderDelete(context.Background(), "test-docker") + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err := f.DevPodProviderDelete(cleanupCtx, "test-docker") framework.ExpectNoError(err) }) @@ -68,8 +68,8 @@ var _ = ginkgo.Describe( func(ctx context.Context) { f, err := setupDockerProvider(initialDir+"/bin", "docker") framework.ExpectNoError(err) - ginkgo.DeferCleanup(func() { - _ = f.DevPodProviderDelete(context.Background(), "docker") + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + _ = f.DevPodProviderDelete(cleanupCtx, "docker") }) initialList, err := f.DevPodList(ctx) @@ -90,8 +90,8 @@ var _ = ginkgo.Describe( func(ctx context.Context) { f, err := setupDockerProvider(initialDir+"/bin", "docker") framework.ExpectNoError(err) - ginkgo.DeferCleanup(func() { - _ = f.DevPodProviderDelete(context.Background(), "docker") + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + _ = f.DevPodProviderDelete(cleanupCtx, "docker") }) tempDir, err := framework.CopyToTempDir( diff --git a/e2e/tests/up/provider_kubernetes.go b/e2e/tests/up/provider_kubernetes.go index 7bc818233..860ed4384 100644 --- a/e2e/tests/up/provider_kubernetes.go +++ b/e2e/tests/up/provider_kubernetes.go @@ -65,8 +65,8 @@ var _ = ginkgo.Describe( _ = f.DevPodProviderDelete(ctx, "kubernetes") err = f.DevPodProviderAdd(ctx, "kubernetes", "-o", "KUBERNETES_NAMESPACE=devpod") framework.ExpectNoError(err) - ginkgo.DeferCleanup(func() { - err = f.DevPodProviderDelete(context.Background(), "kubernetes") + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err := f.DevPodProviderDelete(cleanupCtx, "kubernetes") framework.ExpectNoError(err) }) diff --git a/e2e/tests/up/up.go b/e2e/tests/up/up.go index d4c236946..be5c77981 100644 --- a/e2e/tests/up/up.go +++ b/e2e/tests/up/up.go @@ -33,7 +33,7 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-workspaces"), fun framework.ExpectNoError(err) name := "vscode-remote-try-python" - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), name) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, name) // Wait for devpod workspace to come online (deadline: 30s) err = f.DevPodUp(ctx, "https://github.com/microsoft/vscode-remote-try-python.git") @@ -156,8 +156,8 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-workspaces"), fun framework.ExpectNoError(err) err = f.DevPodProviderUse(ctx, providerName) framework.ExpectNoError(err) - ginkgo.DeferCleanup(func() { - err = f.DevPodProviderDelete(context.Background(), providerName) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err := f.DevPodProviderDelete(cleanupCtx, providerName) framework.ExpectNoError(err) }) @@ -203,8 +203,8 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-workspaces"), fun framework.ExpectNoError(err) err = f.DevPodProviderUse(ctx, providerName) framework.ExpectNoError(err) - ginkgo.DeferCleanup(func() { - err = f.DevPodProviderDelete(context.Background(), providerName) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err := f.DevPodProviderDelete(cleanupCtx, providerName) framework.ExpectNoError(err) }) @@ -229,8 +229,8 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-workspaces"), fun framework.ExpectNoError(err) err = f.DevPodProviderUse(ctx, providerName) framework.ExpectNoError(err) - ginkgo.DeferCleanup(func() { - err = f.DevPodProviderDelete(context.Background(), providerName) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err := f.DevPodProviderDelete(cleanupCtx, providerName) framework.ExpectNoError(err) }) @@ -268,8 +268,8 @@ var _ = ginkgo.Describe("testing up command", ginkgo.Label("up-workspaces"), fun framework.ExpectNoError(err) err = f.DevPodProviderUse(ctx, providerName) framework.ExpectNoError(err) - ginkgo.DeferCleanup(func() { - err = f.DevPodProviderDelete(context.Background(), providerName) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err := f.DevPodProviderDelete(cleanupCtx, providerName) framework.ExpectNoError(err) }) diff --git a/e2e/tests/up/up_private_token.go b/e2e/tests/up/up_private_token.go index 4045505a1..75e2762a5 100644 --- a/e2e/tests/up/up_private_token.go +++ b/e2e/tests/up/up_private_token.go @@ -44,7 +44,7 @@ var _ = ginkgo.Describe( ) name := "testprivaterepo" - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, context.Background(), name) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, name) err = f.DevPodUp(ctx, "https://github.com/"+username+"/test_private_repo.git") framework.ExpectNoError(err) diff --git a/e2e/tests/upgrade/upgrade.go b/e2e/tests/upgrade/upgrade.go index b97b71138..afbf6845f 100644 --- a/e2e/tests/upgrade/upgrade.go +++ b/e2e/tests/upgrade/upgrade.go @@ -14,6 +14,7 @@ import ( var _ = ginkgo.Describe("testing upgrade command", ginkgo.Label("upgrade"), ginkgo.Ordered, func() { ginkgo.It( "should detect correct binary for current OS and architecture using dry-run", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { initialDir, err := os.Getwd() framework.ExpectNoError(err, "getting current working directory should not error") From ff49fea3316b636147f901fdf7ea8332bb5590fb Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 12 Apr 2026 22:24:35 -0500 Subject: [PATCH 3/6] fix(e2e): add SpecTimeout, fix goroutine leaks, and improve test safety - Add SpecTimeout to 10 It blocks in build.go and ssh.go that lacked timeout protection - Fix goroutine leak in ssh.go port forwarding test by tracking with sync.WaitGroup and registering DeferCleanup - Fix temp dir leak in ide.go by registering DeferCleanup immediately after CopyToTempDir - Fix wsl.go server lifecycle: register DeferCleanup(server.Close) immediately after creation, remove redundant explicit Close() - Add env var validation in up_private_token.go (skip if unset) and register credential file cleanup before writing - Add gomega empty-array assertions in context.go before IDE list loops to prevent false negatives on empty results --- e2e/tests/build/build.go | 329 ++++++++++++++++--------------- e2e/tests/context/context.go | 23 ++- e2e/tests/ide/ide.go | 1 + e2e/tests/ssh/ssh.go | 17 +- e2e/tests/up-features/wsl.go | 3 +- e2e/tests/up/up_private_token.go | 14 +- 6 files changed, 209 insertions(+), 178 deletions(-) diff --git a/e2e/tests/build/build.go b/e2e/tests/build/build.go index dd2ba9a54..b1237d134 100644 --- a/e2e/tests/build/build.go +++ b/e2e/tests/build/build.go @@ -70,81 +70,84 @@ var _ = ginkgo.Describe("devpod build test suite", ginkgo.Label("build"), ginkgo dockerHelper = &docker.DockerHelper{DockerCommand: "docker", Log: log.Default} }) - ginkgo.It("build docker buildx", func(ctx context.Context) { - f := framework.NewDefaultFramework(initialDir + "/bin") - tempDir, err := framework.CopyToTempDir("tests/build/testdata/docker") - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.It("build docker buildx", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + f := framework.NewDefaultFramework(initialDir + "/bin") + tempDir, err := framework.CopyToTempDir("tests/build/testdata/docker") + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - _ = f.DevPodProviderDelete(ctx, "docker") - err = f.DevPodProviderAdd(ctx, "docker") - framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "docker") - framework.ExpectNoError(err) + _ = f.DevPodProviderDelete(ctx, "docker") + err = f.DevPodProviderAdd(ctx, "docker") + framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, "docker") + framework.ExpectNoError(err) - cfg := getDevcontainerConfig(tempDir) + cfg := getDevcontainerConfig(tempDir) - dockerfilePath := tempDir + "/.devcontainer/Dockerfile" - contentToParse, err := prepareDockerfileContent(dockerfilePath) - framework.ExpectNoError(err) + dockerfilePath := tempDir + "/.devcontainer/Dockerfile" + contentToParse, err := prepareDockerfileContent(dockerfilePath) + framework.ExpectNoError(err) - // do the build - platforms := "linux/amd64,linux/arm64" - err = f.DevPodBuild( - ctx, - tempDir, - "--force-build", - "--platform", - platforms, - "--repository", - prebuildRepoName, - "--skip-push", - ) - framework.ExpectNoError(err) + // do the build + platforms := "linux/amd64,linux/arm64" + err = f.DevPodBuild( + ctx, + tempDir, + "--force-build", + "--platform", + platforms, + "--repository", + prebuildRepoName, + "--skip-push", + ) + framework.ExpectNoError(err) - // parse the dockerfile - file, err := dockerfile.Parse(contentToParse) - framework.ExpectNoError(err) - info := &config.ImageBuildInfo{Dockerfile: file} - - // make sure images are there - prebuildHash, err := config.CalculatePrebuildHash(config.PrebuildHashParams{ - Config: cfg, - Platform: "linux/amd64", - Architecture: "amd64", - ContextPath: filepath.Dir(cfg.Origin), - DockerfilePath: dockerfilePath, - DockerfileContent: contentToParse, - BuildInfo: info, - Log: log.Default, - }) - framework.ExpectNoError(err) - _, err = dockerHelper.InspectImage(ctx, prebuildRepoName+":"+prebuildHash, false) - framework.ExpectNoError(err) + // parse the dockerfile + file, err := dockerfile.Parse(contentToParse) + framework.ExpectNoError(err) + info := &config.ImageBuildInfo{Dockerfile: file} - prebuildHash, err = config.CalculatePrebuildHash(config.PrebuildHashParams{ - Config: cfg, - Platform: "linux/arm64", - Architecture: "arm64", - ContextPath: filepath.Dir(cfg.Origin), - DockerfilePath: dockerfilePath, - DockerfileContent: contentToParse, - BuildInfo: info, - Log: log.Default, - }) - framework.ExpectNoError(err) + // make sure images are there + prebuildHash, err := config.CalculatePrebuildHash(config.PrebuildHashParams{ + Config: cfg, + Platform: "linux/amd64", + Architecture: "amd64", + ContextPath: filepath.Dir(cfg.Origin), + DockerfilePath: dockerfilePath, + DockerfileContent: contentToParse, + BuildInfo: info, + Log: log.Default, + }) + framework.ExpectNoError(err) + _, err = dockerHelper.InspectImage(ctx, prebuildRepoName+":"+prebuildHash, false) + framework.ExpectNoError(err) - details, err := dockerHelper.InspectImage(ctx, prebuildRepoName+":"+prebuildHash, false) - framework.ExpectNoError(err) - framework.ExpectEqual( - details.Config.Labels["test"], - "VALUE", - "should contain test label", - ) - }) + prebuildHash, err = config.CalculatePrebuildHash(config.PrebuildHashParams{ + Config: cfg, + Platform: "linux/arm64", + Architecture: "arm64", + ContextPath: filepath.Dir(cfg.Origin), + DockerfilePath: dockerfilePath, + DockerfileContent: contentToParse, + BuildInfo: info, + Log: log.Default, + }) + framework.ExpectNoError(err) + + details, err := dockerHelper.InspectImage(ctx, prebuildRepoName+":"+prebuildHash, false) + framework.ExpectNoError(err) + framework.ExpectEqual( + details.Config.Labels["test"], + "VALUE", + "should contain test label", + ) + }) ginkgo.It( "should build image without repository specified if skip-push flag is set", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { f := framework.NewDefaultFramework(initialDir + "/bin") tempDir, err := framework.CopyToTempDir("tests/build/testdata/docker") @@ -196,6 +199,7 @@ var _ = ginkgo.Describe("devpod build test suite", ginkgo.Label("build"), ginkgo ginkgo.It( "should build the image of the referenced service from the docker compose file", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { f := framework.NewDefaultFramework(initialDir + "/bin") tempDir, err := framework.CopyToTempDir("tests/build/testdata/docker-compose") @@ -220,6 +224,7 @@ var _ = ginkgo.Describe("devpod build test suite", ginkgo.Label("build"), ginkgo ginkgo.It( "should build docker-compose with features when build context differs from devcontainer location", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { f := framework.NewDefaultFramework(initialDir + "/bin") tempDir, err := framework.CopyToTempDir( @@ -241,114 +246,122 @@ var _ = ginkgo.Describe("devpod build test suite", ginkgo.Label("build"), ginkgo }, ) - ginkgo.It("build docker internal buildkit", func(ctx context.Context) { - f := framework.NewDefaultFramework(initialDir + "/bin") - tempDir, err := framework.CopyToTempDir("tests/build/testdata/docker") - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.It("build docker internal buildkit", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + f := framework.NewDefaultFramework(initialDir + "/bin") + tempDir, err := framework.CopyToTempDir("tests/build/testdata/docker") + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - _ = f.DevPodProviderDelete(ctx, "docker") - err = f.DevPodProviderAdd(ctx, "docker") - framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "docker") - framework.ExpectNoError(err) + _ = f.DevPodProviderDelete(ctx, "docker") + err = f.DevPodProviderAdd(ctx, "docker") + framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, "docker") + framework.ExpectNoError(err) - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) - cfg := getDevcontainerConfig(tempDir) + cfg := getDevcontainerConfig(tempDir) - dockerfilePath := tempDir + "/.devcontainer/Dockerfile" - contentToParse, err := prepareDockerfileContent(dockerfilePath) - framework.ExpectNoError(err) + dockerfilePath := tempDir + "/.devcontainer/Dockerfile" + contentToParse, err := prepareDockerfileContent(dockerfilePath) + framework.ExpectNoError(err) - prebuildRepo := prebuildRepoName - - // do the build - err = f.DevPodBuild( - ctx, - tempDir, - "--force-build", - "--force-internal-buildkit", - "--repository", - prebuildRepo, - "--skip-push", - ) - framework.ExpectNoError(err) + prebuildRepo := prebuildRepoName - // parse the dockerfile - file, err := dockerfile.Parse(contentToParse) - framework.ExpectNoError(err) - info := &config.ImageBuildInfo{Dockerfile: file} - - // make sure images are there - prebuildHash, err := config.CalculatePrebuildHash(config.PrebuildHashParams{ - Config: cfg, - Platform: "linux/" + runtime.GOARCH, - Architecture: runtime.GOARCH, - ContextPath: filepath.Dir(cfg.Origin), - DockerfilePath: dockerfilePath, - DockerfileContent: contentToParse, - BuildInfo: info, - Log: log.Default, - }) - framework.ExpectNoError(err) + // do the build + err = f.DevPodBuild( + ctx, + tempDir, + "--force-build", + "--force-internal-buildkit", + "--repository", + prebuildRepo, + "--skip-push", + ) + framework.ExpectNoError(err) - _, err = dockerHelper.InspectImage(ctx, prebuildRepo+":"+prebuildHash, false) - framework.ExpectNoError(err) - }) + // parse the dockerfile + file, err := dockerfile.Parse(contentToParse) + framework.ExpectNoError(err) + info := &config.ImageBuildInfo{Dockerfile: file} - ginkgo.It("build kubernetes dockerless", func(ctx context.Context) { - if runtime.GOOS == osWindows { - ginkgo.Skip("skipping on windows") - } + // make sure images are there + prebuildHash, err := config.CalculatePrebuildHash(config.PrebuildHashParams{ + Config: cfg, + Platform: "linux/" + runtime.GOARCH, + Architecture: runtime.GOARCH, + ContextPath: filepath.Dir(cfg.Origin), + DockerfilePath: dockerfilePath, + DockerfileContent: contentToParse, + BuildInfo: info, + Log: log.Default, + }) + framework.ExpectNoError(err) - f := framework.NewDefaultFramework(initialDir + "/bin") - tempDir, err := framework.CopyToTempDir("tests/build/testdata/kubernetes") - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + _, err = dockerHelper.InspectImage(ctx, prebuildRepo+":"+prebuildHash, false) + framework.ExpectNoError(err) + }) - _ = f.DevPodProviderDelete(ctx, "kubernetes") - err = f.DevPodProviderAdd(ctx, "kubernetes") - framework.ExpectNoError(err) - err = f.DevPodProviderUse( - ctx, - "kubernetes", - "-o", - "KUBERNETES_NAMESPACE=devpod", - ) - framework.ExpectNoError(err) + ginkgo.It("build kubernetes dockerless", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + if runtime.GOOS == osWindows { + ginkgo.Skip("skipping on windows") + } - ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) + f := framework.NewDefaultFramework(initialDir + "/bin") + tempDir, err := framework.CopyToTempDir("tests/build/testdata/kubernetes") + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - // do the up - err = f.DevPodUp(ctx, tempDir) - framework.ExpectNoError(err) + _ = f.DevPodProviderDelete(ctx, "kubernetes") + err = f.DevPodProviderAdd(ctx, "kubernetes") + framework.ExpectNoError(err) + err = f.DevPodProviderUse( + ctx, + "kubernetes", + "-o", + "KUBERNETES_NAMESPACE=devpod", + ) + framework.ExpectNoError(err) - // check if ssh works - out, err := f.DevPodSSH(ctx, tempDir, "echo -n $MY_TEST") - framework.ExpectNoError(err) - framework.ExpectEqual(out, "test456", "should contain my-test") - }) + ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, tempDir) - ginkgo.It("rebuild kubernetes dockerless", func(ctx context.Context) { - validateKubernetesDeploymentWithoutDocker( - ctx, - initialDir, - func(ctx context.Context, f *framework.Framework, tempDir string) error { - return f.DevPodUpRecreate(ctx, tempDir) - }, - ) - }) + // do the up + err = f.DevPodUp(ctx, tempDir) + framework.ExpectNoError(err) - ginkgo.It("reset kubernetes dockerless", func(ctx context.Context) { - validateKubernetesDeploymentWithoutDocker( - ctx, - initialDir, - func(ctx context.Context, f *framework.Framework, tempDir string) error { - return f.DevPodUpReset(ctx, tempDir) - }, - ) - }) + // check if ssh works + out, err := f.DevPodSSH(ctx, tempDir, "echo -n $MY_TEST") + framework.ExpectNoError(err) + framework.ExpectEqual(out, "test456", "should contain my-test") + }) + + ginkgo.It("rebuild kubernetes dockerless", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + validateKubernetesDeploymentWithoutDocker( + ctx, + initialDir, + func(ctx context.Context, f *framework.Framework, tempDir string) error { + return f.DevPodUpRecreate(ctx, tempDir) + }, + ) + }) + + ginkgo.It("reset kubernetes dockerless", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + validateKubernetesDeploymentWithoutDocker( + ctx, + initialDir, + func(ctx context.Context, f *framework.Framework, tempDir string) error { + return f.DevPodUpReset(ctx, tempDir) + }, + ) + }) }) func validateKubernetesDeploymentWithoutDocker( diff --git a/e2e/tests/context/context.go b/e2e/tests/context/context.go index ab6639e33..20b186ebf 100644 --- a/e2e/tests/context/context.go +++ b/e2e/tests/context/context.go @@ -6,6 +6,7 @@ import ( "os" "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" "github.com/skevetter/devpod/e2e/framework" ) @@ -79,6 +80,7 @@ var _ = ginkgo.Describe( var ides []map[string]any err = json.Unmarshal([]byte(output), &ides) framework.ExpectNoError(err) + gomega.Expect(ides).NotTo(gomega.BeEmpty(), "IDE list should not be empty") for _, ide := range ides { if ide["name"] == ideIntelliJ { @@ -93,6 +95,8 @@ var _ = ginkgo.Describe( err = json.Unmarshal([]byte(output), &ides) framework.ExpectNoError(err) + gomega.Expect(ides). + NotTo(gomega.BeEmpty(), "IDE list for context-b should not be empty") intellijFound := false for _, ide := range ides { @@ -103,9 +107,9 @@ var _ = ginkgo.Describe( } } } - if !intellijFound { - ginkgo.Fail("IDE was not set in context-b as expected") - } + gomega.Expect(intellijFound).To( + gomega.BeTrue(), "IDE should be set as default in context-b", + ) ginkgo.GinkgoT().Setenv("DEVPOD_CONTEXT", contextB) @@ -114,6 +118,10 @@ var _ = ginkgo.Describe( err = json.Unmarshal([]byte(output), &ides) framework.ExpectNoError(err) + gomega.Expect(ides).NotTo( + gomega.BeEmpty(), + "IDE list via DEVPOD_CONTEXT should not be empty", + ) intellijFound = false for _, ide := range ides { @@ -124,11 +132,10 @@ var _ = ginkgo.Describe( } } } - if !intellijFound { - ginkgo.Fail( - "Selecting context-b using environment variable DEVPOD_CONTEXT does not work as expected", - ) - } + gomega.Expect(intellijFound).To( + gomega.BeTrue(), + "DEVPOD_CONTEXT env var should select context-b with intellij as default IDE", + ) }, ) }, diff --git a/e2e/tests/ide/ide.go b/e2e/tests/ide/ide.go index c639153f7..b7ae3961c 100644 --- a/e2e/tests/ide/ide.go +++ b/e2e/tests/ide/ide.go @@ -21,6 +21,7 @@ var _ = ginkgo.Describe("devpod ide test suite", ginkgo.Label("ide"), ginkgo.Ord f := framework.NewDefaultFramework(initialDir + "/bin") tempDir, err := framework.CopyToTempDir("tests/ide/testdata") framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) err = f.DevPodProviderAdd(ctx, "docker") framework.ExpectNoError(err) diff --git a/e2e/tests/ssh/ssh.go b/e2e/tests/ssh/ssh.go index ae495bcb0..7bc39200c 100644 --- a/e2e/tests/ssh/ssh.go +++ b/e2e/tests/ssh/ssh.go @@ -10,6 +10,7 @@ import ( "runtime" "strconv" "strings" + "sync" "time" "github.com/onsi/ginkgo/v2" @@ -30,6 +31,7 @@ var _ = ginkgo.Describe("devpod ssh test suite", ginkgo.Label("ssh"), ginkgo.Ord ginkgo.It( "should start a new workspace with a docker provider (default) and SSH into it", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { tempDir, err := framework.CopyToTempDir("tests/ssh/testdata/local-test") framework.ExpectNoError(err) @@ -346,6 +348,7 @@ var _ = ginkgo.Describe("devpod ssh test suite", ginkgo.Label("ssh"), ginkgo.Ord ginkgo.It( "should start a new workspace with a docker provider (default) and forward a port into it", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { if runtime.GOOS == osWindows { ginkgo.Skip("skipping on windows") @@ -385,9 +388,10 @@ var _ = ginkgo.Describe("devpod ssh test suite", ginkgo.Label("ssh"), ginkgo.Ord ) err = serverCmd.Start() framework.ExpectNoError(err) - go func() { + var wg sync.WaitGroup + wg.Go(func() { _ = serverCmd.Wait() - }() + }) portForwardCtx, cancelPort := context.WithTimeout( ctx, @@ -396,9 +400,14 @@ var _ = ginkgo.Describe("devpod ssh test suite", ginkgo.Label("ssh"), ginkgo.Ord defer cancelPort() ginkgo.GinkgoWriter.Println("Starting port forwarding for port", port) - go func() { + wg.Go(func() { _ = f.DevpodPortTest(portForwardCtx, strconv.Itoa(port), tempDir) - }() + }) + ginkgo.DeferCleanup(func() { + serverCancel() + cancelPort() + wg.Wait() + }) ginkgo.GinkgoWriter.Println("Polling for port", port, "to be accessible") address := net.JoinHostPort("localhost", strconv.Itoa(port)) diff --git a/e2e/tests/up-features/wsl.go b/e2e/tests/up-features/wsl.go index 119962313..6ffd18d3d 100644 --- a/e2e/tests/up-features/wsl.go +++ b/e2e/tests/up-features/wsl.go @@ -29,6 +29,7 @@ var _ = ginkgo.Describe( ginkgo.It("should use http headers to download feature", func(ctx context.Context) { server := ghttp.NewServer() + ginkgo.DeferCleanup(server.Close) tempDir, err := framework.CopyToTempDir( "tests/up-features/testdata/docker-features-http-headers", @@ -51,7 +52,6 @@ var _ = ginkgo.Describe( framework.ExpectNoError(err) ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - ginkgo.DeferCleanup(server.Close) respHeader := http.Header{} respHeader.Set( @@ -82,7 +82,6 @@ var _ = ginkgo.Describe( // Wait for devpod workspace to come online (deadline: 30s) err = f.DevPodUp(ctx, tempDir) framework.ExpectNoError(err) - server.Close() }, ginkgo.SpecTimeout(framework.GetTimeout())) ginkgo.It( diff --git a/e2e/tests/up/up_private_token.go b/e2e/tests/up/up_private_token.go index 75e2762a5..b72524d2a 100644 --- a/e2e/tests/up/up_private_token.go +++ b/e2e/tests/up/up_private_token.go @@ -25,23 +25,25 @@ var _ = ginkgo.Describe( ginkgo.It("should allow checkout of a private GitRepo", func(ctx context.Context) { username := os.Getenv("GH_USERNAME") token := os.Getenv("GH_ACCESS_TOKEN") + if username == "" || token == "" { + ginkgo.Skip("GH_USERNAME and GH_ACCESS_TOKEN must be set") + } f, err := setupDockerProvider(initialDir+"/bin", "docker") framework.ExpectNoError(err) + // Register credential cleanup before writing to ensure cleanup on any failure + credentialPath := filepath.Join(os.Getenv("HOME"), ".git-credentials") + ginkgo.DeferCleanup(func() { _ = os.Remove(credentialPath) }) + // setup git credentials err = exec.Command("git", []string{"config", "--global", "credential.helper", "store"}...). Run() framework.ExpectNoError(err) gitCredentialString := []byte("https://" + username + ":" + token + "@github.com") - err = os.WriteFile( - filepath.Join(os.Getenv("HOME"), ".git-credentials"), - gitCredentialString, 0o600) + err = os.WriteFile(credentialPath, gitCredentialString, 0o600) framework.ExpectNoError(err) - ginkgo.DeferCleanup( - func() { _ = os.Remove(filepath.Join(os.Getenv("HOME"), ".git-credentials")) }, - ) name := "testprivaterepo" ginkgo.DeferCleanup(f.DevPodWorkspaceDelete, name) From fb91df8884c0b3032ae66756b556e9085c6a9386 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 12 Apr 2026 22:29:43 -0500 Subject: [PATCH 4/6] fix(e2e): add SpecTimeout to provider tests and fix cleanup issues - Add SpecTimeout to 11 It blocks in provider.go missing timeout protection - Remove duplicate DeferCleanup in machineprovider.go that would delete the workspace twice - Standardize SpecTimeout position in machineprovider.go (move from trailing to inline with It declaration) - Remove bare defer in provider_podman.go BeforeEach - Fix DeferCleanup ordering in provider_docker.go so Stop runs before Remove (LIFO order) --- e2e/tests/machineprovider/machineprovider.go | 254 +++++------ e2e/tests/provider/provider.go | 444 ++++++++++--------- e2e/tests/up/provider_docker.go | 2 +- e2e/tests/up/provider_podman.go | 7 +- 4 files changed, 365 insertions(+), 342 deletions(-) diff --git a/e2e/tests/machineprovider/machineprovider.go b/e2e/tests/machineprovider/machineprovider.go index 1611bd111..e0238e8f3 100644 --- a/e2e/tests/machineprovider/machineprovider.go +++ b/e2e/tests/machineprovider/machineprovider.go @@ -26,148 +26,148 @@ var _ = ginkgo.Describe( framework.ExpectNoError(err) }) - ginkgo.It("test start / stop / status", func(ctx context.Context) { - f := framework.NewDefaultFramework(initialDir + "/bin") + ginkgo.It("test start / stop / status", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + f := framework.NewDefaultFramework(initialDir + "/bin") + + // copy test dir + tempDir, err := framework.CopyToTempDirWithoutChdir( + initialDir + "/tests/machineprovider/testdata/machineprovider", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - // copy test dir - tempDir, err := framework.CopyToTempDirWithoutChdir( - initialDir + "/tests/machineprovider/testdata/machineprovider", - ) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + // create docker provider + err = f.DevPodProviderAdd( + ctx, + filepath.Join(tempDir, "provider.yaml"), + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err = f.DevPodWorkspaceDelete(cleanupCtx, tempDir) + framework.ExpectNoError(err) + }) - // create docker provider - err = f.DevPodProviderAdd( - ctx, - filepath.Join(tempDir, "provider.yaml"), - ) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(func(cleanupCtx context.Context) { - err = f.DevPodWorkspaceDelete(cleanupCtx, tempDir) + // wait for devpod workspace to come online (deadline: 30s) + err = f.DevPodUp(ctx, tempDir, "--debug") framework.ExpectNoError(err) - }) - ginkgo.DeferCleanup(func(cleanupCtx context.Context) { - err = f.DevPodWorkspaceDelete(cleanupCtx, tempDir) + + // expect workspace + workspace, err := f.FindWorkspace(ctx, tempDir) framework.ExpectNoError(err) - }) - // wait for devpod workspace to come online (deadline: 30s) - err = f.DevPodUp(ctx, tempDir, "--debug") - framework.ExpectNoError(err) + // check status + status, err := f.DevPodStatus(ctx, tempDir) + framework.ExpectNoError(err) + framework.ExpectEqual( + strings.ToUpper(status.State), + "RUNNING", + "workspace status did not match", + ) + + // stop container + err = f.DevPodStop(ctx, tempDir) + framework.ExpectNoError(err) - // expect workspace - workspace, err := f.FindWorkspace(ctx, tempDir) - framework.ExpectNoError(err) + // check status + status, err = f.DevPodStatus(ctx, tempDir) + framework.ExpectNoError(err) + framework.ExpectEqual( + strings.ToUpper(status.State), + "STOPPED", + "workspace status did not match", + ) + + // wait for devpod workspace to come online (deadline: 30s) + err = f.DevPodUp(ctx, tempDir) + framework.ExpectNoError(err) - // check status - status, err := f.DevPodStatus(ctx, tempDir) - framework.ExpectNoError(err) - framework.ExpectEqual( - strings.ToUpper(status.State), - "RUNNING", - "workspace status did not match", - ) - - // stop container - err = f.DevPodStop(ctx, tempDir) - framework.ExpectNoError(err) + // check if ssh works as it should start the container + out, err := f.DevPodSSH( + ctx, + tempDir, + fmt.Sprintf("cat /workspaces/%s/test.txt", workspace.ID), + ) + framework.ExpectNoError(err) + framework.ExpectEqual( + strings.TrimSpace(out), + "Test123", + "workspace content does not match", + ) + }) - // check status - status, err = f.DevPodStatus(ctx, tempDir) - framework.ExpectNoError(err) - framework.ExpectEqual( - strings.ToUpper(status.State), - "STOPPED", - "workspace status did not match", - ) - - // wait for devpod workspace to come online (deadline: 30s) - err = f.DevPodUp(ctx, tempDir) - framework.ExpectNoError(err) + ginkgo.It("test devpod inactivity timeout", + ginkgo.SpecTimeout(framework.GetTimeout()*5), + func(ctx context.Context) { + f := framework.NewDefaultFramework(initialDir + "/bin") - // check if ssh works as it should start the container - out, err := f.DevPodSSH( - ctx, - tempDir, - fmt.Sprintf("cat /workspaces/%s/test.txt", workspace.ID), - ) - framework.ExpectNoError(err) - framework.ExpectEqual( - strings.TrimSpace(out), - "Test123", - "workspace content does not match", - ) - }, ginkgo.SpecTimeout(framework.GetTimeout())) - - ginkgo.It("test devpod inactivity timeout", func(ctx context.Context) { - f := framework.NewDefaultFramework(initialDir + "/bin") - - // copy test dir - tempDir, err := framework.CopyToTempDirWithoutChdir( - initialDir + "/tests/machineprovider/testdata/machineprovider2", - ) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + // copy test dir + tempDir, err := framework.CopyToTempDirWithoutChdir( + initialDir + "/tests/machineprovider/testdata/machineprovider2", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - // create provider - _ = f.DevPodProviderDelete(ctx, "docker123") - err = f.DevPodProviderAdd(ctx, filepath.Join(tempDir, "provider.yaml")) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(func(cleanupCtx context.Context) { - err = f.DevPodWorkspaceDelete(cleanupCtx, tempDir) + // create provider + _ = f.DevPodProviderDelete(ctx, "docker123") + err = f.DevPodProviderAdd(ctx, filepath.Join(tempDir, "provider.yaml")) framework.ExpectNoError(err) - err = f.DevPodProviderDelete(cleanupCtx, "docker123") + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err = f.DevPodWorkspaceDelete(cleanupCtx, tempDir) + framework.ExpectNoError(err) + err = f.DevPodProviderDelete(cleanupCtx, "docker123") + framework.ExpectNoError(err) + }) + + // wait for devpod workspace to come online (deadline: 30s) + err = f.DevPodUp(ctx, tempDir, "--debug", "--daemon-interval=3s") framework.ExpectNoError(err) - }) - // wait for devpod workspace to come online (deadline: 30s) - err = f.DevPodUp(ctx, tempDir, "--debug", "--daemon-interval=3s") - framework.ExpectNoError(err) - - // check status - status, err := f.DevPodStatus(ctx, tempDir, "--container-status=false") - framework.ExpectNoError(err) - framework.ExpectEqual( - strings.ToUpper(status.State), - "RUNNING", - "workspace status did not match", - ) - - // stop container - err = f.DevPodStop(ctx, tempDir) - framework.ExpectNoError(err) + // check status + status, err := f.DevPodStatus(ctx, tempDir, "--container-status=false") + framework.ExpectNoError(err) + framework.ExpectEqual( + strings.ToUpper(status.State), + "RUNNING", + "workspace status did not match", + ) + + // stop container + err = f.DevPodStop(ctx, tempDir) + framework.ExpectNoError(err) - // check status - status, err = f.DevPodStatus(ctx, tempDir, "--container-status=false") - framework.ExpectNoError(err) - framework.ExpectEqual( - strings.ToUpper(status.State), - "STOPPED", - "workspace status did not match", - ) - - // wait for devpod workspace to come online (deadline: 30s) - err = f.DevPodUp(ctx, tempDir, "--daemon-interval=3s") - framework.ExpectNoError(err) + // check status + status, err = f.DevPodStatus(ctx, tempDir, "--container-status=false") + framework.ExpectNoError(err) + framework.ExpectEqual( + strings.ToUpper(status.State), + "STOPPED", + "workspace status did not match", + ) + + // wait for devpod workspace to come online (deadline: 30s) + err = f.DevPodUp(ctx, tempDir, "--daemon-interval=3s") + framework.ExpectNoError(err) - // check status - status, err = f.DevPodStatus(ctx, tempDir, "--container-status=false") - framework.ExpectNoError(err) - framework.ExpectEqual( - strings.ToUpper(status.State), - "RUNNING", - "workspace status did not match", - ) - - // wait until workspace is stopped again - gomega.Eventually(func() string { - status, err := f.DevPodStatus(ctx, tempDir, "--container-status=false") + // check status + status, err = f.DevPodStatus(ctx, tempDir, "--container-status=false") framework.ExpectNoError(err) - return strings.ToUpper(status.State) - }, time.Minute*2, time.Second*2).Should( - gomega.Equal("STOPPED"), - "machine did not shutdown in time", - ) - }, ginkgo.SpecTimeout(framework.GetTimeout()*5)) + framework.ExpectEqual( + strings.ToUpper(status.State), + "RUNNING", + "workspace status did not match", + ) + + // wait until workspace is stopped again + gomega.Eventually(func() string { + status, err := f.DevPodStatus(ctx, tempDir, "--container-status=false") + framework.ExpectNoError(err) + return strings.ToUpper(status.State) + }, time.Minute*2, time.Second*2).Should( + gomega.Equal("STOPPED"), + "machine did not shutdown in time", + ) + }) }, ) diff --git a/e2e/tests/provider/provider.go b/e2e/tests/provider/provider.go index 13895d421..05b48d1c1 100644 --- a/e2e/tests/provider/provider.go +++ b/e2e/tests/provider/provider.go @@ -34,132 +34,140 @@ var _ = ginkgo.Describe( framework.ExpectNoError(err) }) - ginkgo.It("should add simple provider and delete it", func(ctx context.Context) { - tempDir, err := framework.CopyToTempDir( - "tests/provider/testdata/simple-k8s-provider", - ) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.It("should add simple provider and delete it", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + tempDir, err := framework.CopyToTempDir( + "tests/provider/testdata/simple-k8s-provider", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - f := framework.NewDefaultFramework(initialDir + "/bin") + f := framework.NewDefaultFramework(initialDir + "/bin") - err = f.DevPodProviderDelete(ctx, "provider1", "--ignore-not-found") - framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, "provider1", "--ignore-not-found") + framework.ExpectNoError(err) - err = f.DevPodProviderAdd(ctx, tempDir+"/provider1.yaml") - framework.ExpectNoError(err) + err = f.DevPodProviderAdd(ctx, tempDir+"/provider1.yaml") + framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "provider1") - framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "providerX") - framework.ExpectError(err) + err = f.DevPodProviderUse(ctx, "provider1") + framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, "providerX") + framework.ExpectError(err) - err = f.DevPodProviderDelete(ctx, "provider1") - framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, "provider1") + framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "provider1") - framework.ExpectError(err) - }) + err = f.DevPodProviderUse(ctx, "provider1") + framework.ExpectError(err) + }) - ginkgo.It("should add simple provider and update it", func(ctx context.Context) { - tempDir, err := framework.CopyToTempDir( - "tests/provider/testdata/simple-k8s-provider", - ) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.It("should add simple provider and update it", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + tempDir, err := framework.CopyToTempDir( + "tests/provider/testdata/simple-k8s-provider", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - f := framework.NewDefaultFramework(initialDir + "/bin") + f := framework.NewDefaultFramework(initialDir + "/bin") - err = f.DevPodProviderDelete(ctx, "provider2", "--ignore-not-found") - framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, "provider2", "--ignore-not-found") + framework.ExpectNoError(err) - err = f.DevPodProviderAdd(ctx, tempDir+"/provider2.yaml") - framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "provider2") - framework.ExpectNoError(err) + err = f.DevPodProviderAdd(ctx, tempDir+"/provider2.yaml") + framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, "provider2") + framework.ExpectNoError(err) - checkCtx, cancel := context.WithDeadline( - ctx, - time.Now().Add(30*time.Second), - ) - err = f.DevPodProviderOptionsCheckNamespaceDescription( - checkCtx, - "provider2", - "The namespace to use", - ) - framework.ExpectNoError(err) - cancel() + checkCtx, cancel := context.WithDeadline( + ctx, + time.Now().Add(30*time.Second), + ) + err = f.DevPodProviderOptionsCheckNamespaceDescription( + checkCtx, + "provider2", + "The namespace to use", + ) + framework.ExpectNoError(err) + cancel() - err = f.DevPodProviderUpdate( - ctx, - "provider2", - tempDir+"/provider2-update.yaml", - ) - framework.ExpectNoError(err) + err = f.DevPodProviderUpdate( + ctx, + "provider2", + tempDir+"/provider2-update.yaml", + ) + framework.ExpectNoError(err) - checkCtx, cancel = context.WithDeadline( - ctx, - time.Now().Add(30*time.Second), - ) - err = f.DevPodProviderOptionsCheckNamespaceDescription( - checkCtx, - "provider2", - "Updated namespace parameter", - ) - framework.ExpectNoError(err) - cancel() + checkCtx, cancel = context.WithDeadline( + ctx, + time.Now().Add(30*time.Second), + ) + err = f.DevPodProviderOptionsCheckNamespaceDescription( + checkCtx, + "provider2", + "Updated namespace parameter", + ) + framework.ExpectNoError(err) + cancel() - err = f.DevPodProviderDelete(ctx, "provider2") - framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, "provider2") + framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "provider2") - framework.ExpectError(err) - }) + err = f.DevPodProviderUse(ctx, "provider2") + framework.ExpectError(err) + }) - ginkgo.It("should list all providers", func(ctx context.Context) { - tempDir, err := framework.CopyToTempDir( - "tests/provider/testdata/simple-k8s-provider", - ) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.It("should list all providers", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + tempDir, err := framework.CopyToTempDir( + "tests/provider/testdata/simple-k8s-provider", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - f := framework.NewDefaultFramework(initialDir + "/bin") + f := framework.NewDefaultFramework(initialDir + "/bin") - err = f.DevPodProviderDelete(ctx, "provider1", "--ignore-not-found") - framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, "provider1", "--ignore-not-found") + framework.ExpectNoError(err) - err = f.DevPodProviderAdd(ctx, tempDir+"/provider1.yaml") - framework.ExpectNoError(err) + err = f.DevPodProviderAdd(ctx, tempDir+"/provider1.yaml") + framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "provider1") - framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, "provider1") + framework.ExpectNoError(err) - err = os.WriteFile(tempDir+"/.DS_Store", []byte("test"), 0o644) // #nosec G306 - framework.ExpectNoError(err) + err = os.WriteFile(tempDir+"/.DS_Store", []byte("test"), 0o644) // #nosec G306 + framework.ExpectNoError(err) - err = f.DevPodProviderList(ctx) - framework.ExpectNoError(err) + err = f.DevPodProviderList(ctx) + framework.ExpectNoError(err) - err = f.DevPodProviderDelete(ctx, "provider1") - framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, "provider1") + framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "provider1") - framework.ExpectError(err) - }) + err = f.DevPodProviderUse(ctx, "provider1") + framework.ExpectError(err) + }) - ginkgo.It("should parse options", func(ctx context.Context) { - tempDir, err := framework.CopyToTempDir( - "tests/provider/testdata/simple-k8s-provider", - ) - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.It("should parse options", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + tempDir, err := framework.CopyToTempDir( + "tests/provider/testdata/simple-k8s-provider", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - f := framework.NewDefaultFramework(initialDir + "/bin") + f := framework.NewDefaultFramework(initialDir + "/bin") - err = f.DevPodProviderDelete(ctx, "provider3", "--ignore-not-found") - framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, "provider3", "--ignore-not-found") + framework.ExpectNoError(err) - podManifest := ` + podManifest := ` apiVersion: v1 kind: Pod metadata: @@ -168,65 +176,70 @@ spec: containers: - name: devpod ` - err = f.DevPodProviderAdd( - ctx, - tempDir+"/provider3.yaml", - "--option=TEMPLATE="+podManifest, - ) - framework.ExpectNoError(err) + err = f.DevPodProviderAdd( + ctx, + tempDir+"/provider3.yaml", + "--option=TEMPLATE="+podManifest, + ) + framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "provider3") - framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, "provider3") + framework.ExpectNoError(err) - err = f.DevPodProviderFindOption(ctx, "provider3", podManifest) - framework.ExpectNoError(err) + err = f.DevPodProviderFindOption(ctx, "provider3", podManifest) + framework.ExpectNoError(err) - err = f.DevPodProviderDelete(ctx, "provider3") - framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, "provider3") + framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, "provider3") - framework.ExpectError(err) - }) + err = f.DevPodProviderUse(ctx, "provider3") + framework.ExpectError(err) + }) // RENAME-1. - ginkgo.It("should rename a provider to a new, valid name", func(ctx context.Context) { - tempDir, err := framework.CopyToTempDir("tests/provider/testdata/simple-k8s-provider") - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.It("should rename a provider to a new, valid name", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + tempDir, err := framework.CopyToTempDir( + "tests/provider/testdata/simple-k8s-provider", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - f := framework.NewDefaultFramework(initialDir + "/bin") + f := framework.NewDefaultFramework(initialDir + "/bin") - providerName := "provider-rename1" - renamedProviderName := "provider-renamed" + providerName := "provider-rename1" + renamedProviderName := "provider-renamed" - // Ensure that provider is deleted. - err = f.DevPodProviderDelete(ctx, providerName, "--ignore-not-found") - framework.ExpectNoError(err) - err = f.DevPodProviderDelete(ctx, renamedProviderName, "--ignore-not-found") - framework.ExpectNoError(err) + // Ensure that provider is deleted. + err = f.DevPodProviderDelete(ctx, providerName, "--ignore-not-found") + framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, renamedProviderName, "--ignore-not-found") + framework.ExpectNoError(err) - // Add provider. - err = f.DevPodProviderAdd(ctx, tempDir+"/provider1.yaml", "--name", providerName) - framework.ExpectNoError(err) + // Add provider. + err = f.DevPodProviderAdd(ctx, tempDir+"/provider1.yaml", "--name", providerName) + framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, providerName) - framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, providerName) + framework.ExpectNoError(err) - err = f.DevPodProviderRename(ctx, providerName, renamedProviderName) - framework.ExpectNoError(err) + err = f.DevPodProviderRename(ctx, providerName, renamedProviderName) + framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, providerName) - framework.ExpectError(err) - err = f.DevPodProviderUse(ctx, renamedProviderName) - framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, providerName) + framework.ExpectError(err) + err = f.DevPodProviderUse(ctx, renamedProviderName) + framework.ExpectNoError(err) - err = f.DevPodProviderDelete(ctx, renamedProviderName) - framework.ExpectNoError(err) - }) + err = f.DevPodProviderDelete(ctx, renamedProviderName) + framework.ExpectNoError(err) + }) // RENAME-2. ginkgo.It( "should fail to rename a provider to a name that already exists", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { tempDir, err := framework.CopyToTempDir( "tests/provider/testdata/simple-k8s-provider", @@ -278,22 +291,25 @@ spec: }, ) - ginkgo.It("should fail to rename a non-existent provider", func(ctx context.Context) { - f := framework.NewDefaultFramework(initialDir + "/bin") + ginkgo.It("should fail to rename a non-existent provider", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + f := framework.NewDefaultFramework(initialDir + "/bin") - nonExistentProvider := "non-existent-provider3" - newName := "new-name3" + nonExistentProvider := "non-existent-provider3" + newName := "new-name3" - err := f.DevPodProviderDelete(ctx, nonExistentProvider, "--ignore-not-found") - framework.ExpectNoError(err) + err := f.DevPodProviderDelete(ctx, nonExistentProvider, "--ignore-not-found") + framework.ExpectNoError(err) - // Attempt to rename non-existent provider. - err = f.DevPodProviderRename(ctx, nonExistentProvider, newName) - framework.ExpectError(err) - }) + // Attempt to rename non-existent provider. + err = f.DevPodProviderRename(ctx, nonExistentProvider, newName) + framework.ExpectError(err) + }) ginkgo.It( "should rename a provider with an associated running workspace", + ginkgo.SpecTimeout(framework.GetTimeout()), func(ctx context.Context) { f := framework.NewDefaultFramework(initialDir + "/bin") @@ -370,86 +386,92 @@ spec: }, ) - ginkgo.It("should fail to rename a provider to an invalid name", func(ctx context.Context) { - tempDir, err := framework.CopyToTempDir("tests/provider/testdata/simple-k8s-provider") - framework.ExpectNoError(err) - ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + ginkgo.It("should fail to rename a provider to an invalid name", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + tempDir, err := framework.CopyToTempDir( + "tests/provider/testdata/simple-k8s-provider", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) - f := framework.NewDefaultFramework(initialDir + "/bin") + f := framework.NewDefaultFramework(initialDir + "/bin") - providerName := "provider-to-rename-invalid6" + providerName := "provider-to-rename-invalid6" - err = f.DevPodProviderDelete(ctx, providerName, "--ignore-not-found") - framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, providerName, "--ignore-not-found") + framework.ExpectNoError(err) - err = f.DevPodProviderAdd(ctx, tempDir+"/provider1.yaml", "--name", providerName) - framework.ExpectNoError(err) + err = f.DevPodProviderAdd(ctx, tempDir+"/provider1.yaml", "--name", providerName) + framework.ExpectNoError(err) - err = f.DevPodProviderRename(ctx, providerName, "invalid/name6") - framework.ExpectError(err) + err = f.DevPodProviderRename(ctx, providerName, "invalid/name6") + framework.ExpectError(err) - err = f.DevPodProviderUse(ctx, providerName) - framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, providerName) + framework.ExpectNoError(err) - err = f.DevPodProviderDelete(ctx, providerName) - framework.ExpectNoError(err) - }) + err = f.DevPodProviderDelete(ctx, providerName) + framework.ExpectNoError(err) + }) - ginkgo.It("should preserve provider options after rename", func(ctx context.Context) { - f := framework.NewDefaultFramework(initialDir + "/bin") + ginkgo.It("should preserve provider options after rename", + ginkgo.SpecTimeout(framework.GetTimeout()), + func(ctx context.Context) { + f := framework.NewDefaultFramework(initialDir + "/bin") - providerName := "provider-opts-rename7" - renamedName := "renamed-opts-rename7" + providerName := "provider-opts-rename7" + renamedName := "renamed-opts-rename7" - err := f.DevPodProviderDelete(ctx, providerName, "--ignore-not-found") - framework.ExpectNoError(err) - err = f.DevPodProviderDelete(ctx, renamedName, "--ignore-not-found") - framework.ExpectNoError(err) + err := f.DevPodProviderDelete(ctx, providerName, "--ignore-not-found") + framework.ExpectNoError(err) + err = f.DevPodProviderDelete(ctx, renamedName, "--ignore-not-found") + framework.ExpectNoError(err) - err = addDockerProvider(ctx, f, providerName) - framework.ExpectNoError(err) - err = f.DevPodProviderUse(ctx, providerName) - framework.ExpectNoError(err) + err = addDockerProvider(ctx, f, providerName) + framework.ExpectNoError(err) + err = f.DevPodProviderUse(ctx, providerName) + framework.ExpectNoError(err) - beforeJSON, err := f.DevPodProviderOptionsJSON(ctx, providerName) - framework.ExpectNoError(err) + beforeJSON, err := f.DevPodProviderOptionsJSON(ctx, providerName) + framework.ExpectNoError(err) - var beforeOpts map[string]any - err = json.Unmarshal([]byte(beforeJSON), &beforeOpts) - framework.ExpectNoError(err) + var beforeOpts map[string]any + err = json.Unmarshal([]byte(beforeJSON), &beforeOpts) + framework.ExpectNoError(err) - err = f.DevPodProviderRename(ctx, providerName, renamedName) - framework.ExpectNoError(err) + err = f.DevPodProviderRename(ctx, providerName, renamedName) + framework.ExpectNoError(err) - afterJSON, err := f.DevPodProviderOptionsJSON(ctx, renamedName) - framework.ExpectNoError(err) + afterJSON, err := f.DevPodProviderOptionsJSON(ctx, renamedName) + framework.ExpectNoError(err) - var afterOpts map[string]any - err = json.Unmarshal([]byte(afterJSON), &afterOpts) - framework.ExpectNoError(err) + var afterOpts map[string]any + err = json.Unmarshal([]byte(afterJSON), &afterOpts) + framework.ExpectNoError(err) - for key, beforeVal := range beforeOpts { - afterVal, exists := afterOpts[key] - gomega.Expect(exists). - To(gomega.BeTrue(), "option %s should exist after rename", key) - - beforeMap := beforeVal.(map[string]any) - afterMap := afterVal.(map[string]any) - - beforeV, hasBefore := beforeMap["value"] - afterV, hasAfter := afterMap["value"] - gomega.Expect(hasAfter).To(gomega.Equal(hasBefore), - "option %s value presence should be preserved", key) - if hasBefore { - gomega.Expect(afterV).To(gomega.Equal(beforeV), - "option %s value should be preserved", key) + for key, beforeVal := range beforeOpts { + afterVal, exists := afterOpts[key] + gomega.Expect(exists). + To(gomega.BeTrue(), "option %s should exist after rename", key) + + beforeMap := beforeVal.(map[string]any) + afterMap := afterVal.(map[string]any) + + beforeV, hasBefore := beforeMap["value"] + afterV, hasAfter := afterMap["value"] + gomega.Expect(hasAfter).To(gomega.Equal(hasBefore), + "option %s value presence should be preserved", key) + if hasBefore { + gomega.Expect(afterV).To(gomega.Equal(beforeV), + "option %s value should be preserved", key) + } } - } - err = f.DevPodProviderUse(ctx, providerName) - framework.ExpectError(err) + err = f.DevPodProviderUse(ctx, providerName) + framework.ExpectError(err) - err = f.DevPodProviderDelete(ctx, renamedName) - framework.ExpectNoError(err) - }) + err = f.DevPodProviderDelete(ctx, renamedName) + framework.ExpectNoError(err) + }) }) diff --git a/e2e/tests/up/provider_docker.go b/e2e/tests/up/provider_docker.go index 4903ae32c..5e32b9cf3 100644 --- a/e2e/tests/up/provider_docker.go +++ b/e2e/tests/up/provider_docker.go @@ -79,8 +79,8 @@ var _ = ginkgo.Describe( return err == nil && containerDetails[0].State.Running }).WithTimeout(30 * time.Second).WithPolling(1 * time.Second).Should(gomega.BeTrue()) - ginkgo.DeferCleanup(dtc.dockerHelper.Remove, ids[0]) ginkgo.DeferCleanup(dtc.dockerHelper.Stop, ids[0]) + ginkgo.DeferCleanup(dtc.dockerHelper.Remove, ids[0]) var containerDetails []container.InspectResponse err = dtc.dockerHelper.Inspect(ctx, ids, "container", &containerDetails) diff --git a/e2e/tests/up/provider_podman.go b/e2e/tests/up/provider_podman.go index 20dd38692..fa8e475c6 100644 --- a/e2e/tests/up/provider_podman.go +++ b/e2e/tests/up/provider_podman.go @@ -50,10 +50,11 @@ var _ = ginkgo.Describe( wrapper, err := os.Create(initialDir + "/bin/podman-rootful") framework.ExpectNoError(err) - defer func() { _ = wrapper.Close() }() - _, err = wrapper.WriteString("#!/bin/sh\nsudo podman \"$@\"\n") - framework.ExpectNoError(err) + if err != nil { + _ = wrapper.Close() + framework.ExpectNoError(err) + } err = wrapper.Close() framework.ExpectNoError(err) From f230f5de979b0054e1e2a1c96d43ddc6063d3592 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Mon, 13 Apr 2026 00:06:01 -0500 Subject: [PATCH 5/6] fix(e2e): swap DeferCleanup order so Stop runs before Remove DeferCleanup callbacks execute in LIFO order. The previous ordering registered Stop first and Remove second, so Remove ran before Stop, causing "container is running" errors during cleanup. --- e2e/tests/up/provider_docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/tests/up/provider_docker.go b/e2e/tests/up/provider_docker.go index 5e32b9cf3..4903ae32c 100644 --- a/e2e/tests/up/provider_docker.go +++ b/e2e/tests/up/provider_docker.go @@ -79,8 +79,8 @@ var _ = ginkgo.Describe( return err == nil && containerDetails[0].State.Running }).WithTimeout(30 * time.Second).WithPolling(1 * time.Second).Should(gomega.BeTrue()) - ginkgo.DeferCleanup(dtc.dockerHelper.Stop, ids[0]) ginkgo.DeferCleanup(dtc.dockerHelper.Remove, ids[0]) + ginkgo.DeferCleanup(dtc.dockerHelper.Stop, ids[0]) var containerDetails []container.InspectResponse err = dtc.dockerHelper.Inspect(ctx, ids, "container", &containerDetails) From 7400d19644dc7e2b25a3366be6b905c93aa78c96 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Mon, 13 Apr 2026 08:39:01 -0500 Subject: [PATCH 6/6] fix(e2e): handle DevPodProviderAdd error and add missing provider cleanup - SetupDockerProvider now returns the error from DevPodProviderAdd instead of silently discarding it. - Add missing DeferCleanup for mock-provider in the second machine spec, matching the pattern used in the first spec. --- e2e/framework/command.go | 9 ++++++++- e2e/tests/machine/machine.go | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/e2e/framework/command.go b/e2e/framework/command.go index ac66a810b..5e4105803 100644 --- a/e2e/framework/command.go +++ b/e2e/framework/command.go @@ -462,6 +462,13 @@ func (f *Framework) DevPodIDEList(ctx context.Context, extraArgs ...string) (str func SetupDockerProvider(binDir, dockerPath string) (*Framework, error) { f := NewDefaultFramework(binDir) _ = f.DevPodProviderDelete(context.Background(), "docker") - _ = f.DevPodProviderAdd(context.Background(), "docker", "-o", "DOCKER_PATH="+dockerPath) + if err := f.DevPodProviderAdd( + context.Background(), + "docker", + "-o", + "DOCKER_PATH="+dockerPath, + ); err != nil { + return nil, fmt.Errorf("failed to add docker provider: %w", err) + } return f, f.DevPodProviderUse(context.Background(), "docker") } diff --git a/e2e/tests/machine/machine.go b/e2e/tests/machine/machine.go index d3deff1b9..9f5012575 100644 --- a/e2e/tests/machine/machine.go +++ b/e2e/tests/machine/machine.go @@ -72,6 +72,10 @@ var _ = ginkgo.Describe("devpod testing machine", ginkgo.Label("machine"), ginkg ginkgo.By("Add mock provider") err = f.DevPodProviderAdd(ctx, tempDir+"/mock-provider.yaml") framework.ExpectNoError(err) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err = f.DevPodProviderDelete(cleanupCtx, "mock-provider") + framework.ExpectNoError(err) + }) ginkgo.By("Use mock provider") err = f.DevPodProviderUse(ctx, "mock-provider")