Skip to content

Commit 2d8e0ec

Browse files
committed
Forward host environment variables to the emulator container
1 parent 87a0335 commit 2d8e0ec

File tree

3 files changed

+56
-69
lines changed

3 files changed

+56
-69
lines changed

internal/container/start.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ func Start(ctx context.Context, rt runtime.Runtime, sink output.Sink, opts Start
9696

9797
tel := opts.Telemetry
9898

99+
var hostEnv []string
100+
for _, e := range os.Environ() {
101+
if strings.HasPrefix(e, "CI=") || (strings.HasPrefix(e, "LOCALSTACK_") && !strings.HasPrefix(e, "LOCALSTACK_AUTH_TOKEN=")) {
102+
hostEnv = append(hostEnv, e)
103+
}
104+
}
105+
99106
containers := make([]runtime.ContainerConfig, len(opts.Containers))
100107
for i, c := range opts.Containers {
101108
image, err := c.Image()
@@ -128,6 +135,8 @@ func Start(ctx context.Context, rt runtime.Runtime, sink output.Sink, opts Start
128135
"MAIN_CONTAINER_NAME="+containerName,
129136
)
130137

138+
env = append(env, hostEnv...)
139+
131140
var binds []runtime.BindMount
132141
if socketPath := rt.SocketPath(); socketPath != "" {
133142
binds = append(binds, runtime.BindMount{HostPath: socketPath, ContainerPath: "/var/run/docker.sock"})

internal/container/start_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"errors"
77
"io"
8+
"strings"
89
"testing"
910

1011
"github.com/localstack/lstk/internal/log"
@@ -64,3 +65,26 @@ func TestServicePortRange_ReturnsExpectedPorts(t *testing.T) {
6465
assert.Equal(t, "4559", ports[50].ContainerPort)
6566
assert.Equal(t, "4559", ports[50].HostPort)
6667
}
68+
69+
func TestFilterHostEnv(t *testing.T) {
70+
tests := []struct {
71+
name string
72+
env string
73+
expected bool
74+
}{
75+
{"CI is included", "CI=true", true},
76+
{"LOCALSTACK_DISABLE_EVENTS is included", "LOCALSTACK_DISABLE_EVENTS=1", true},
77+
{"LOCALSTACK_HOST is included", "LOCALSTACK_HOST=0.0.0.0", true},
78+
{"LOCALSTACK_AUTH_TOKEN is excluded", "LOCALSTACK_AUTH_TOKEN=secret", false},
79+
{"PATH is excluded", "PATH=/usr/bin", false},
80+
{"HOME is excluded", "HOME=/root", false},
81+
{"LOCALSTACK_BUILD_VERSION is included", "LOCALSTACK_BUILD_VERSION=3.0.0", true},
82+
}
83+
84+
for _, tt := range tests {
85+
t.Run(tt.name, func(t *testing.T) {
86+
got := strings.HasPrefix(tt.env, "CI=") || (strings.HasPrefix(tt.env, "LOCALSTACK_") && !strings.HasPrefix(tt.env, "LOCALSTACK_AUTH_TOKEN="))
87+
assert.Equal(t, tt.expected, got)
88+
})
89+
}
90+
}

test/integration/start_test.go

Lines changed: 23 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,13 @@ package integration_test
22

33
import (
44
"context"
5-
"crypto/tls"
6-
"fmt"
75
"net"
8-
"net/http"
96
"os"
107
"path/filepath"
11-
"strconv"
128
"strings"
139
"testing"
1410

1511
"github.com/docker/docker/api/types/container"
16-
"github.com/docker/go-connections/nat"
1712
"github.com/localstack/lstk/test/integration/env"
1813
"github.com/stretchr/testify/assert"
1914
"github.com/stretchr/testify/require"
@@ -164,66 +159,36 @@ func TestStartCommandSetsUpContainerCorrectly(t *testing.T) {
164159
assert.Equal(t, containerName, envVars["MAIN_CONTAINER_NAME"])
165160
assert.NotEmpty(t, envVars["LOCALSTACK_AUTH_TOKEN"])
166161
})
162+
}
167163

168-
t.Run("docker socket mount", func(t *testing.T) {
169-
if !strings.HasPrefix(dockerClient.DaemonHost(), "unix://") {
170-
t.Skip("Docker daemon is not reachable via unix socket")
171-
}
172-
173-
assert.True(t, hasBindTarget(inspect.HostConfig.Binds, "/var/run/docker.sock"),
174-
"expected Docker socket bind mount to /var/run/docker.sock, got: %v", inspect.HostConfig.Binds)
175-
176-
envVars := containerEnvToMap(inspect.Config.Env)
177-
assert.Equal(t, "unix:///var/run/docker.sock", envVars["DOCKER_HOST"])
178-
})
164+
func TestStartCommandPassesCIAndLocalStackEnvVars(t *testing.T) {
165+
requireDocker(t)
166+
_ = env.Require(t, env.AuthToken)
179167

180-
t.Run("service port range", func(t *testing.T) {
181-
for p := 4510; p <= 4559; p++ {
182-
port := nat.Port(fmt.Sprintf("%d/tcp", p))
183-
bindings := inspect.HostConfig.PortBindings[port]
184-
if assert.NotEmpty(t, bindings, "port %d/tcp should be bound", p) {
185-
assert.Equal(t, strconv.Itoa(p), bindings[0].HostPort)
186-
}
187-
}
188-
})
168+
cleanup()
169+
t.Cleanup(cleanup)
189170

190-
t.Run("main port", func(t *testing.T) {
191-
mainBindings := inspect.HostConfig.PortBindings[nat.Port("4566/tcp")]
192-
require.NotEmpty(t, mainBindings, "port 4566/tcp should be bound")
193-
assert.Equal(t, "4566", mainBindings[0].HostPort)
194-
})
171+
t.Setenv("CI", "true")
172+
t.Setenv("LOCALSTACK_DISABLE_EVENTS", "1")
173+
t.Setenv("LOCALSTACK_AUTH_TOKEN", "host-token")
195174

196-
t.Run("https port", func(t *testing.T) {
197-
httpsBindings := inspect.HostConfig.PortBindings[nat.Port("443/tcp")]
198-
require.NotEmpty(t, httpsBindings, "port 443/tcp should be bound")
199-
assert.Equal(t, "443", httpsBindings[0].HostPort)
200-
})
175+
mockServer := createMockLicenseServer(true)
176+
defer mockServer.Close()
201177

202-
t.Run("volume mount", func(t *testing.T) {
203-
assert.True(t, hasBindTarget(inspect.HostConfig.Binds, "/var/lib/localstack"),
204-
"expected volume bind mount to /var/lib/localstack, got: %v", inspect.HostConfig.Binds)
205-
})
178+
ctx := testContext(t)
179+
_, stderr, err := runLstk(t, ctx, "", env.With(env.APIEndpoint, mockServer.URL), "start")
180+
require.NoError(t, err, "lstk start failed: %s", stderr)
181+
requireExitCode(t, 0, err)
206182

207-
t.Run("http health endpoint", func(t *testing.T) {
208-
resp, err := http.Get("http://localhost.localstack.cloud:4566/_localstack/health")
209-
require.NoError(t, err)
210-
defer resp.Body.Close()
211-
assert.Equal(t, http.StatusOK, resp.StatusCode)
212-
})
183+
inspect, err := dockerClient.ContainerInspect(ctx, containerName)
184+
require.NoError(t, err, "failed to inspect container")
185+
require.True(t, inspect.State.Running)
213186

214-
t.Run("https health endpoint", func(t *testing.T) {
215-
// LS certificate is not in system trust store
216-
// But cert validity is out of scope here: use InsecureSkipVerify
217-
client := &http.Client{
218-
Transport: &http.Transport{
219-
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
220-
},
221-
}
222-
resp, err := client.Get("https://localhost.localstack.cloud/_localstack/health")
223-
require.NoError(t, err)
224-
defer resp.Body.Close()
225-
assert.Equal(t, http.StatusOK, resp.StatusCode)
226-
})
187+
envVars := containerEnvToMap(inspect.Config.Env)
188+
assert.Equal(t, "true", envVars["CI"])
189+
assert.Equal(t, "1", envVars["LOCALSTACK_DISABLE_EVENTS"])
190+
assert.NotEmpty(t, envVars["LOCALSTACK_AUTH_TOKEN"])
191+
assert.NotEqual(t, "host-token", envVars["LOCALSTACK_AUTH_TOKEN"], "host LOCALSTACK_AUTH_TOKEN should not be passed through")
227192
}
228193

229194
// containerEnvToMap converts a Docker container's []string env to a map.
@@ -236,17 +201,6 @@ func containerEnvToMap(envList []string) map[string]string {
236201
return m
237202
}
238203

239-
// hasBindTarget checks if any bind mount targets the given container path.
240-
func hasBindTarget(binds []string, containerPath string) bool {
241-
for _, b := range binds {
242-
parts := strings.Split(b, ":")
243-
if len(parts) >= 2 && parts[1] == containerPath {
244-
return true
245-
}
246-
}
247-
return false
248-
}
249-
250204
func cleanup() {
251205
ctx := context.Background()
252206
_ = dockerClient.ContainerStop(ctx, containerName, container.StopOptions{})

0 commit comments

Comments
 (0)