From bab1429b053ad2e2d44f82389a606b58695e8162 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 26 Apr 2026 09:52:38 -0500 Subject: [PATCH 1/3] test(e2e): add tunnel and pipe lifecycle E2E tests --- .github/workflows/pr-ci.yml | 6 + e2e/e2e_suite_test.go | 1 + .../tunnel/testdata/tunnel/.devcontainer.json | 4 + .../tunnel/testdata/tunnel/provider.yaml | 44 +++++++ e2e/tests/tunnel/testdata/tunnel/test.txt | 1 + e2e/tests/tunnel/tunnel.go | 114 ++++++++++++++++++ 6 files changed, 170 insertions(+) create mode 100644 e2e/tests/tunnel/testdata/tunnel/.devcontainer.json create mode 100644 e2e/tests/tunnel/testdata/tunnel/provider.yaml create mode 100644 e2e/tests/tunnel/testdata/tunnel/test.txt create mode 100644 e2e/tests/tunnel/tunnel.go diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 683539002..2fbd42603 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -156,6 +156,12 @@ jobs: install-kind: false requires-secret: false + - label: tunnel + runner: ubuntu-latest + free-disk-space: false + install-kind: false + requires-secret: false + - label: provider runner: ubuntu-latest free-disk-space: false diff --git a/e2e/e2e_suite_test.go b/e2e/e2e_suite_test.go index 788f434e9..4460e21ed 100644 --- a/e2e/e2e_suite_test.go +++ b/e2e/e2e_suite_test.go @@ -17,6 +17,7 @@ import ( _ "github.com/devsy-org/devsy/e2e/tests/machineprovider" _ "github.com/devsy-org/devsy/e2e/tests/provider" _ "github.com/devsy-org/devsy/e2e/tests/ssh" + _ "github.com/devsy-org/devsy/e2e/tests/tunnel" _ "github.com/devsy-org/devsy/e2e/tests/up" _ "github.com/devsy-org/devsy/e2e/tests/up-features" _ "github.com/devsy-org/devsy/e2e/tests/upgrade" diff --git a/e2e/tests/tunnel/testdata/tunnel/.devcontainer.json b/e2e/tests/tunnel/testdata/tunnel/.devcontainer.json new file mode 100644 index 000000000..001a58b23 --- /dev/null +++ b/e2e/tests/tunnel/testdata/tunnel/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "name": "Go", + "image": "ubuntu" +} diff --git a/e2e/tests/tunnel/testdata/tunnel/provider.yaml b/e2e/tests/tunnel/testdata/tunnel/provider.yaml new file mode 100644 index 000000000..8562dc515 --- /dev/null +++ b/e2e/tests/tunnel/testdata/tunnel/provider.yaml @@ -0,0 +1,44 @@ +name: docker123 +version: 0.0.1 +description: |- + Devsy on Kubernetes +options: + NAMESPACE: + description: The namespace to use + default: devsy-e2e + INACTIVITY_TIMEOUT: + description: The timeout until the pod will be stopped + default: 300s +agent: + path: /usr/local/bin/devsy + inactivityTimeout: ${INACTIVITY_TIMEOUT} + exec: + shutdown: |- + kill 1 +exec: + command: |- + docker exec -i devsy-${MACHINE_ID} sh -c "${COMMAND}" + stop: |- + docker stop devsy-${MACHINE_ID} + start: |- + docker start devsy-${MACHINE_ID} + sleep 5 + status: |- + STATUS=$(docker inspect devsy-${MACHINE_ID} 2>/dev/null | ${DEVSY} helper json get "[0].State.Status" || true) + if [ -z $STATUS ]; then + echo "NOTFOUND" + else + if [ "$STATUS" = "exited" ]; then + echo "STOPPED" + elif [ "$STATUS" = "running" ]; then + echo "RUNNING" + else + echo "BUSY" + fi + fi + create: |- + docker run -d --privileged --name devsy-${MACHINE_ID} docker + docker exec devsy-${MACHINE_ID} mkdir /etc/init.d + sleep 5 + delete: |- + docker stop devsy-${MACHINE_ID} && docker rm devsy-${MACHINE_ID} diff --git a/e2e/tests/tunnel/testdata/tunnel/test.txt b/e2e/tests/tunnel/testdata/tunnel/test.txt new file mode 100644 index 000000000..8f214dd3d --- /dev/null +++ b/e2e/tests/tunnel/testdata/tunnel/test.txt @@ -0,0 +1 @@ +Test123 diff --git a/e2e/tests/tunnel/tunnel.go b/e2e/tests/tunnel/tunnel.go new file mode 100644 index 000000000..71d40c208 --- /dev/null +++ b/e2e/tests/tunnel/tunnel.go @@ -0,0 +1,114 @@ +package tunnel + +import ( + "context" + "os" + "path/filepath" + "strings" + "time" + + "github.com/devsy-org/devsy/e2e/framework" + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +var _ = ginkgo.Describe( + "devsy tunnel test suite", + ginkgo.Label("tunnel"), + ginkgo.Ordered, + func() { + var initialDir string + + ginkgo.BeforeEach(func() { + var err error + initialDir, err = os.Getwd() + framework.ExpectNoError(err) + }) + + ginkgo.It("should tunnel to workspace and exchange data bidirectionally", + ginkgo.SpecTimeout(framework.GetTimeout()*3), + func(ctx context.Context) { + f := framework.NewDefaultFramework(initialDir + "/bin") + + tempDir, err := framework.CopyToTempDirWithoutChdir( + initialDir + "/tests/tunnel/testdata/tunnel", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + + _ = f.DevsyProviderDelete(ctx, "docker123") + err = f.DevsyProviderAdd(ctx, filepath.Join(tempDir, "provider.yaml")) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err = f.DevsyWorkspaceDelete(cleanupCtx, tempDir) + framework.ExpectNoError(err) + err = f.DevsyProviderDelete(cleanupCtx, "docker123") + framework.ExpectNoError(err) + }) + + err = f.DevsyUp(ctx, tempDir, "--debug") + framework.ExpectNoError(err) + + out, err := f.DevsySSH( + ctx, + tempDir, + "echo TestTunnelData123 | cat", + ) + framework.ExpectNoError(err) + gomega.Expect(out).To( + gomega.ContainSubstring("TestTunnelData123"), + "bidirectional tunnel data exchange failed", + ) + + status, err := f.DevsyStatus(ctx, tempDir, "--container-status=false") + framework.ExpectNoError(err) + framework.ExpectEqual( + strings.ToUpper(status.State), + "RUNNING", + "workspace should remain running after SSH session", + ) + }) + + ginkgo.It("should keep workspace running when stdin closes before stdout", + ginkgo.SpecTimeout(framework.GetTimeout()*3), + func(ctx context.Context) { + f := framework.NewDefaultFramework(initialDir + "/bin") + + tempDir, err := framework.CopyToTempDirWithoutChdir( + initialDir + "/tests/tunnel/testdata/tunnel", + ) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir) + + _ = f.DevsyProviderDelete(ctx, "docker123") + err = f.DevsyProviderAdd(ctx, filepath.Join(tempDir, "provider.yaml")) + framework.ExpectNoError(err) + ginkgo.DeferCleanup(func(cleanupCtx context.Context) { + err = f.DevsyWorkspaceDelete(cleanupCtx, tempDir) + framework.ExpectNoError(err) + err = f.DevsyProviderDelete(cleanupCtx, "docker123") + framework.ExpectNoError(err) + }) + + err = f.DevsyUp(ctx, tempDir, "--debug") + framework.ExpectNoError(err) + + out, err := f.DevsySSH(ctx, tempDir, "echo alive") + framework.ExpectNoError(err) + gomega.Expect(out).To( + gomega.ContainSubstring("alive"), + "SSH command should produce output", + ) + + // Verify workspace stays running after stdin pipe closes (regression for PR #139 deadlock). + gomega.Consistently(func() string { + status, err := f.DevsyStatus(ctx, tempDir, "--container-status=false") + framework.ExpectNoError(err) + return strings.ToUpper(status.State) + }, 15*time.Second, 2*time.Second).Should( + gomega.Equal("RUNNING"), + "workspace must stay running after stdin closes", + ) + }) + }, +) From de2b4148cd035db2b3ed3411891f0482b88ddee6 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 26 Apr 2026 09:58:44 -0500 Subject: [PATCH 2/3] test(e2e): remove unused testdata and simplify comment --- e2e/tests/tunnel/testdata/tunnel/test.txt | 1 - e2e/tests/tunnel/tunnel.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 e2e/tests/tunnel/testdata/tunnel/test.txt diff --git a/e2e/tests/tunnel/testdata/tunnel/test.txt b/e2e/tests/tunnel/testdata/tunnel/test.txt deleted file mode 100644 index 8f214dd3d..000000000 --- a/e2e/tests/tunnel/testdata/tunnel/test.txt +++ /dev/null @@ -1 +0,0 @@ -Test123 diff --git a/e2e/tests/tunnel/tunnel.go b/e2e/tests/tunnel/tunnel.go index 71d40c208..ab722baf7 100644 --- a/e2e/tests/tunnel/tunnel.go +++ b/e2e/tests/tunnel/tunnel.go @@ -100,7 +100,7 @@ var _ = ginkgo.Describe( "SSH command should produce output", ) - // Verify workspace stays running after stdin pipe closes (regression for PR #139 deadlock). + // Verify workspace stays running after stdin pipe closes. gomega.Consistently(func() string { status, err := f.DevsyStatus(ctx, tempDir, "--container-status=false") framework.ExpectNoError(err) From ef5b451611ba5deb7348b4229d901807d0819615 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 26 Apr 2026 10:05:24 -0500 Subject: [PATCH 3/3] test(e2e): use mirrored registry image for tunnel testdata --- e2e/tests/tunnel/testdata/tunnel/.devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/tests/tunnel/testdata/tunnel/.devcontainer.json b/e2e/tests/tunnel/testdata/tunnel/.devcontainer.json index 001a58b23..74298f54a 100644 --- a/e2e/tests/tunnel/testdata/tunnel/.devcontainer.json +++ b/e2e/tests/tunnel/testdata/tunnel/.devcontainer.json @@ -1,4 +1,4 @@ { "name": "Go", - "image": "ubuntu" + "image": "ghcr.io/devsy-org/test-images/base:ubuntu" }