Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions e2e/framework/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package framework

import (
"os"

"github.com/onsi/ginkgo/v2"
)

// SetEnvForSpec sets name to value for the current spec. The prior state is
// restored in DeferCleanup; unset and set-to-empty are kept distinct.
func SetEnvForSpec(name, value string) {
restore, err := overrideEnv(name, &value)
ExpectNoError(err)
ginkgo.DeferCleanup(restore)
}

// UnsetEnvForSpec unsets name for the current spec and restores the prior state
// in DeferCleanup.
func UnsetEnvForSpec(name string) {
restore, err := overrideEnv(name, nil)
ExpectNoError(err)
ginkgo.DeferCleanup(restore)
}

// overrideEnv applies value (nil means unset) and returns a restore func that
// returns name to exactly its prior state.
func overrideEnv(name string, value *string) (func(), error) {
previous, hadPrevious := os.LookupEnv(name)
var err error
if value == nil {
err = os.Unsetenv(name)
} else {
err = os.Setenv(name, *value)
}
if err != nil {
return nil, err
}
return func() {
if hadPrevious {
_ = os.Setenv(name, previous)
return
}
_ = os.Unsetenv(name)
}, nil
}
104 changes: 104 additions & 0 deletions e2e/framework/env_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package framework

import (
"os"
"testing"

"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestOverrideEnvRestoresUnsetAfterSet(t *testing.T) {
const name = "DEVSY_E2E_TEST_ENV_UNSET_THEN_SET"
require.NoError(t, os.Unsetenv(name))

restore, err := overrideEnv(name, new("value"))
require.NoError(t, err)
assert.Equal(t, "value", os.Getenv(name))

restore()
_, present := os.LookupEnv(name)
assert.False(t, present, "unset prior state must come back as unset, not empty")
}

func TestOverrideEnvRestoresEmptyAfterSet(t *testing.T) {
const name = "DEVSY_E2E_TEST_ENV_EMPTY_THEN_SET"
t.Setenv(name, "")

restore, err := overrideEnv(name, new("value"))
require.NoError(t, err)
assert.Equal(t, "value", os.Getenv(name))

restore()
got, present := os.LookupEnv(name)
assert.True(t, present, "set-to-empty prior state must stay set")
assert.Empty(t, got)
}

func TestOverrideEnvRestoresPreviousValue(t *testing.T) {
const name = "DEVSY_E2E_TEST_ENV_VALUE_THEN_SET"
t.Setenv(name, "original")

restore, err := overrideEnv(name, new("replacement"))
require.NoError(t, err)
assert.Equal(t, "replacement", os.Getenv(name))

restore()
assert.Equal(t, "original", os.Getenv(name))
}

func TestOverrideEnvUnsetRestoresPreviousValue(t *testing.T) {
const name = "DEVSY_E2E_TEST_ENV_VALUE_THEN_UNSET"
t.Setenv(name, "original")

restore, err := overrideEnv(name, nil)
require.NoError(t, err)
_, present := os.LookupEnv(name)
assert.False(t, present, "nil value must unset the variable")

restore()
assert.Equal(t, "original", os.Getenv(name))
}

func TestEnvSpecHelpers(t *testing.T) {
gomega.RegisterFailHandler(ginkgo.Fail)
ginkgo.RunSpecs(t, "framework env helpers")
}

var _ = ginkgo.Describe("scoped environment helpers", ginkgo.Ordered, func() {
const (
setName = "DEVSY_E2E_TEST_SPEC_ENV_SET_THEN_RESTORE"
unsetName = "DEVSY_E2E_TEST_SPEC_ENV_VALUE_THEN_RESTORE"
)

ginkgo.BeforeAll(func() {
ginkgo.DeferCleanup(os.Unsetenv, setName)
ginkgo.DeferCleanup(os.Unsetenv, unsetName)
gomega.Expect(os.Unsetenv(setName)).To(gomega.Succeed())
gomega.Expect(os.Setenv(unsetName, "original")).To(gomega.Succeed())
})

ginkgo.It("sets the variable for the spec", func() {
SetEnvForSpec(setName, "scoped")
gomega.Expect(os.Getenv(setName)).To(gomega.Equal("scoped"))
})

ginkgo.It("restores the unset prior state after the previous spec", func() {
_, present := os.LookupEnv(setName)
gomega.Expect(present).To(gomega.BeFalse())
})

ginkgo.It("unsets the variable for the spec", func() {
UnsetEnvForSpec(unsetName)
_, present := os.LookupEnv(unsetName)
gomega.Expect(present).To(gomega.BeFalse())
})

ginkgo.It("restores the prior value after the previous spec", func() {
got, present := os.LookupEnv(unsetName)
gomega.Expect(present).To(gomega.BeTrue())
gomega.Expect(got).To(gomega.Equal("original"))
})
})
22 changes: 2 additions & 20 deletions e2e/framework/secrets.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,8 @@
package framework

import (
"os"

"github.com/onsi/ginkgo/v2"
)

// UseFileSecretsBackend forces the file backend so tests do not depend on an OS
// keyring (unavailable in CI); env is restored on cleanup.
func UseFileSecretsBackend() {
for k, v := range map[string]string{
"DEVSY_SECRETS_BACKEND": "file",
"DEVSY_SECRETS_PASSPHRASE": "e2e-passphrase",
} {
prev, had := os.LookupEnv(k)
ExpectNoError(os.Setenv(k, v))
ginkgo.DeferCleanup(func() {
if had {
_ = os.Setenv(k, prev)
} else {
_ = os.Unsetenv(k)
}
})
}
SetEnvForSpec("DEVSY_SECRETS_BACKEND", "file")
SetEnvForSpec("DEVSY_SECRETS_PASSPHRASE", "e2e-passphrase")
}
25 changes: 6 additions & 19 deletions e2e/tests/dockerbuildkit/docker_internal_buildkit.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,19 +118,6 @@ func uniqueTag() string {
return fmt.Sprintf("devsy-internal-buildkit:%d", time.Now().UnixNano())
}

// setProcessEnv overrides a process variable for one spec only.
func setProcessEnv(name, value string) {
previous, had := os.LookupEnv(name)
framework.ExpectNoError(os.Setenv(name, value))
ginkgo.DeferCleanup(func() {
if had {
_ = os.Setenv(name, previous)
} else {
_ = os.Unsetenv(name)
}
})
}

var _ = ginkgo.Describe(
"docker internal buildkit transport",
ginkgo.Label("docker-internal-buildkit"),
Expand Down Expand Up @@ -260,8 +247,8 @@ var _ = ginkgo.Describe(
func(ctx context.Context) {
// Make the process daemon reachable (the same WSL daemon) so a
// silent fallback would succeed.
setProcessEnv("DOCKER_HOST", sshHost)
setProcessEnv(dockerContextEnv, "")
framework.SetEnvForSpec("DOCKER_HOST", sshHost)
framework.SetEnvForSpec(dockerContextEnv, "")

tag := uniqueTag()
ginkgo.DeferCleanup(func(ctx context.Context) {
Expand Down Expand Up @@ -347,8 +334,8 @@ var _ = ginkgo.Describe(
"BuildDevContainer builds on the named Docker context's daemon despite a dead process DOCKER_HOST",
ginkgo.SpecTimeout(framework.TimeoutLong()),
func(ctx context.Context) {
setProcessEnv("DOCKER_HOST", deadProcessHost)
setProcessEnv(dockerContextEnv, "")
framework.SetEnvForSpec("DOCKER_HOST", deadProcessHost)
framework.SetEnvForSpec(dockerContextEnv, "")
_, err := dockerRun(ctx, nil, "info")
gomega.Expect(err).To(gomega.HaveOccurred(),
"process-env daemon must be unreachable for this regression to mean anything")
Expand Down Expand Up @@ -422,8 +409,8 @@ var _ = ginkgo.Describe(
ginkgo.SpecTimeout(framework.TimeoutLong()),
func(ctx context.Context) {
// Make the process daemon reachable so a silent fallback would succeed.
setProcessEnv("DOCKER_HOST", sshHost)
setProcessEnv(dockerContextEnv, "")
framework.SetEnvForSpec("DOCKER_HOST", sshHost)
framework.SetEnvForSpec(dockerContextEnv, "")
gomega.Expect(dockerOutput(ctx, nil, "info", "--format", "{{.ID}}")).
To(gomega.Equal(wslID), "process daemon must be reachable for this negative control")

Expand Down
23 changes: 3 additions & 20 deletions e2e/tests/up-docker-compose/up_docker_compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,6 @@ const (
composeFileSecretsDir = "/run/secrets"
)

func useComposeFileSecretsBackend() {
for name, value := range map[string]string{
"DEVSY_SECRETS_BACKEND": "file",
"DEVSY_SECRETS_PASSPHRASE": "e2e-passphrase",
} {
previous, hadPrevious := os.LookupEnv(name)
framework.ExpectNoError(os.Setenv(name, value))
ginkgo.DeferCleanup(func() {
if hadPrevious {
_ = os.Setenv(name, previous)
} else {
_ = os.Unsetenv(name)
}
})
}
}

var _ = ginkgo.Describe(
"devsy up docker compose test suite",
ginkgo.Label("up-docker-compose"),
Expand All @@ -73,7 +56,7 @@ var _ = ginkgo.Describe(
ginkgo.It(
"migrates an existing Compose workspace when a terminal secret is attached later",
func(ctx context.Context) {
useComposeFileSecretsBackend()
framework.UseFileSecretsBackend()
contextName := fmt.Sprintf("compose-late-secret-%d", time.Now().UnixNano())
framework.ExpectNoError(tc.f.DevsyContextCreate(ctx, contextName))
ginkgo.DeferCleanup(func(cleanupCtx context.Context) {
Expand Down Expand Up @@ -164,7 +147,7 @@ var _ = ginkgo.Describe(
ginkgo.It(
"refreshes saved Compose files for a new file secret after the app container is removed",
func(ctx context.Context) {
useComposeFileSecretsBackend()
framework.UseFileSecretsBackend()
tempDir, workspace, err := tc.setupAndStartWorkspace(
ctx,
"tests/up-docker-compose/testdata/docker-compose-shutdown-action-container",
Expand Down Expand Up @@ -231,7 +214,7 @@ var _ = ginkgo.Describe(
ginkgo.It(
"injects attached terminal secrets through a tmpfs mount",
func(ctx context.Context) {
useComposeFileSecretsBackend()
framework.UseFileSecretsBackend()
contextName := fmt.Sprintf("compose-secret-%d", time.Now().UnixNano())
framework.ExpectNoError(tc.f.DevsyContextCreate(ctx, contextName))
ginkgo.DeferCleanup(func(cleanupCtx context.Context) {
Expand Down
37 changes: 7 additions & 30 deletions e2e/tests/up/sops.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,10 @@ var _ = ginkgo.Describe(
"sops-failure",
filepath.Join(tempDir, "secrets.enc.yaml"),
)
framework.ExpectNoError(os.Setenv("SOPS_AGE_KEY", ""))
framework.ExpectNoError(
os.Setenv("SOPS_AGE_KEY_FILE", filepath.Join(tempDir, "missing-age-key")),
framework.SetEnvForSpec("SOPS_AGE_KEY", "")
framework.SetEnvForSpec(
"SOPS_AGE_KEY_FILE",
filepath.Join(tempDir, "missing-age-key"),
)

stdout, stderr, upErr := dtc.f.DevsyUpStreams(
Expand All @@ -170,33 +171,9 @@ var _ = ginkgo.Describe(
)

func useSOPSAgeIdentity() {
setSOPSEnv("SOPS_AGE_KEY", sopsE2EAgeIdentity)
unsetSOPSEnv("SOPS_AGE_KEY_FILE")
unsetSOPSEnv("SOPS_AGE_KEY_CMD")
}

func setSOPSEnv(name, value string) {
previous, had := os.LookupEnv(name)
framework.ExpectNoError(os.Setenv(name, value))
ginkgo.DeferCleanup(func() {
if had {
_ = os.Setenv(name, previous)
return
}
_ = os.Unsetenv(name)
})
}

func unsetSOPSEnv(name string) {
previous, had := os.LookupEnv(name)
framework.ExpectNoError(os.Unsetenv(name))
ginkgo.DeferCleanup(func() {
if had {
_ = os.Setenv(name, previous)
return
}
_ = os.Unsetenv(name)
})
framework.SetEnvForSpec("SOPS_AGE_KEY", sopsE2EAgeIdentity)
framework.UnsetEnvForSpec("SOPS_AGE_KEY_FILE")
framework.UnsetEnvForSpec("SOPS_AGE_KEY_CMD")
}

func registerSOPSSource(ctx context.Context, dtc *dockerTestContext, name, filePath string) {
Expand Down
Loading