Skip to content

Commit 51e54f2

Browse files
authored
fix: ensure prefixed commands do not clobber legacy default values (#190)
1 parent 62879ef commit 51e54f2

File tree

6 files changed

+110
-53
lines changed

6 files changed

+110
-53
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,8 @@ On MacOS or Windows systems, we recommend either using a VM or the provided `.de
306306
| Flag | Environment variable | Default | Description |
307307
| - | - | - | - |
308308
| `--setup-script` | `ENVBUILDER_SETUP_SCRIPT` | | The script to run before the init script. It runs as the root user regardless of the user specified in the devcontainer.json file. SetupScript is ran as the root user prior to the init script. It is used to configure envbuilder dynamically during the runtime. e.g. specifying whether to start systemd or tiny init for PID 1. |
309-
| `--init-script` | `ENVBUILDER_INIT_SCRIPT` | `sleep infinity` | The script to run to initialize the workspace. |
310-
| `--init-command` | `ENVBUILDER_INIT_COMMAND` | `/bin/sh` | The command to run to initialize the workspace. |
309+
| `--init-script` | `ENVBUILDER_INIT_SCRIPT` | | The script to run to initialize the workspace. Default: `sleep infinity`. |
310+
| `--init-command` | `ENVBUILDER_INIT_COMMAND` | | The command to run to initialize the workspace. Default: `/bin/sh`. |
311311
| `--init-args` | `ENVBUILDER_INIT_ARGS` | | The arguments to pass to the init command. They are split according to /bin/sh rules with https://github.com/kballard/go-shellquote. |
312312
| `--cache-repo` | `ENVBUILDER_CACHE_REPO` | | The name of the container registry to push the cache image to. If this is empty, the cache will not be pushed. |
313313
| `--base-image-cache-dir` | `ENVBUILDER_BASE_IMAGE_CACHE_DIR` | | The path to a directory where the base image can be found. This should be a read-only directory solely mounted for the purpose of caching the base image. |

envbuilder.go

+13-7
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ type DockerConfig configfile.ConfigFile
8686
// Filesystem is the filesystem to use for all operations.
8787
// Defaults to the host filesystem.
8888
func Run(ctx context.Context, options Options) error {
89+
// Temporarily removed these from the default settings to prevent conflicts
90+
// between current and legacy environment variables that add default values.
91+
// Once the legacy environment variables are phased out, this can be
92+
// reinstated to the previous default values.
93+
if len(options.IgnorePaths) == 0 {
94+
options.IgnorePaths = []string{"/var/run"}
95+
}
96+
if options.InitScript == "" {
97+
options.InitScript = "sleep infinity"
98+
}
99+
if options.InitCommand == "" {
100+
options.InitCommand = "/bin/sh"
101+
}
89102
// Default to the shell!
90103
initArgs := []string{"-c", options.InitScript}
91104
if options.InitArgs != "" {
@@ -377,13 +390,6 @@ func Run(ctx context.Context, options Options) error {
377390
options.CacheRepo = fmt.Sprintf("localhost:%d/local/cache", tcpAddr.Port)
378391
}
379392

380-
// Temporarily removed this from the default settings to prevent conflicts
381-
// between current and legacy environment variables that add default values.
382-
// Once the legacy environment variables are phased out, this can be
383-
// reinstated to the IGNORE_PATHS default.
384-
if len(options.IgnorePaths) == 0 {
385-
options.IgnorePaths = []string{"/var/run"}
386-
}
387393
// IgnorePaths in the Kaniko options doesn't properly ignore paths.
388394
// So we add them to the default ignore list. See:
389395
// https://github.com/GoogleContainerTools/kaniko/blob/63be4990ca5a60bdf06ddc4d10aa4eca0c0bc714/cmd/executor/cmd/root.go#L136

integration/integration_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"path/filepath"
2020
"strings"
2121
"testing"
22+
"time"
2223

2324
"github.com/coder/envbuilder"
2425
"github.com/coder/envbuilder/devcontainer/features"
@@ -44,6 +45,54 @@ const (
4445
testImageUbuntu = "localhost:5000/envbuilder-test-ubuntu:latest"
4546
)
4647

48+
func TestInitScriptInitCommand(t *testing.T) {
49+
t.Parallel()
50+
51+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
52+
defer cancel()
53+
54+
// Init script will hit the below handler to signify INIT_SCRIPT works.
55+
initCalled := make(chan struct{})
56+
initSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
57+
initCalled <- struct{}{}
58+
w.WriteHeader(http.StatusOK)
59+
}))
60+
61+
srv := createGitServer(t, gitServerOptions{
62+
files: map[string]string{
63+
// Let's say /bin/sh is not available and we can only use /bin/ash
64+
"Dockerfile": fmt.Sprintf("FROM %s\nRUN unlink /bin/sh", testImageAlpine),
65+
},
66+
})
67+
_, err := runEnvbuilder(t, options{env: []string{
68+
envbuilderEnv("GIT_URL", srv.URL),
69+
envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"),
70+
envbuilderEnv("INIT_SCRIPT", fmt.Sprintf(`wget -O - %q`, initSrv.URL)),
71+
envbuilderEnv("INIT_COMMAND", "/bin/ash"),
72+
}})
73+
require.NoError(t, err)
74+
75+
select {
76+
case <-initCalled:
77+
case <-ctx.Done():
78+
}
79+
require.NoError(t, ctx.Err(), "init script did not execute for prefixed env vars")
80+
81+
_, err = runEnvbuilder(t, options{env: []string{
82+
envbuilderEnv("GIT_URL", srv.URL),
83+
envbuilderEnv("DOCKERFILE_PATH", "Dockerfile"),
84+
fmt.Sprintf(`INIT_SCRIPT=wget -O - %q`, initSrv.URL),
85+
`INIT_COMMAND=/bin/ash`,
86+
}})
87+
require.NoError(t, err)
88+
89+
select {
90+
case <-initCalled:
91+
case <-ctx.Done():
92+
}
93+
require.NoError(t, ctx.Err(), "init script did not execute for legacy env vars")
94+
}
95+
4796
func TestForceSafe(t *testing.T) {
4897
t.Parallel()
4998

options.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -156,18 +156,18 @@ func (o *Options) CLI() serpent.OptionSet {
156156
"specifying whether to start systemd or tiny init for PID 1.",
157157
},
158158
{
159-
Flag: "init-script",
160-
Env: WithEnvPrefix("INIT_SCRIPT"),
161-
Default: "sleep infinity",
159+
Flag: "init-script",
160+
Env: WithEnvPrefix("INIT_SCRIPT"),
161+
// Default: "sleep infinity", // TODO: reinstate once legacy opts are removed.
162162
Value: serpent.StringOf(&o.InitScript),
163-
Description: "The script to run to initialize the workspace.",
163+
Description: "The script to run to initialize the workspace. Default: `sleep infinity`.",
164164
},
165165
{
166-
Flag: "init-command",
167-
Env: WithEnvPrefix("INIT_COMMAND"),
168-
Default: "/bin/sh",
166+
Flag: "init-command",
167+
Env: WithEnvPrefix("INIT_COMMAND"),
168+
// Default: "/bin/sh", // TODO: reinstate once legacy opts are removed.
169169
Value: serpent.StringOf(&o.InitCommand),
170-
Description: "The command to run to initialize the workspace.",
170+
Description: "The command to run to initialize the workspace. Default: `/bin/sh`.",
171171
},
172172
{
173173
Flag: "init-args",

options_test.go

+33-32
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/coder/envbuilder"
1010
"github.com/coder/serpent"
11+
"github.com/stretchr/testify/assert"
1112
"github.com/stretchr/testify/require"
1213
)
1314

@@ -68,8 +69,8 @@ func TestEnvOptionParsing(t *testing.T) {
6869
func TestLegacyEnvVars(t *testing.T) {
6970
legacyEnvs := map[string]string{
7071
"SETUP_SCRIPT": "./setup-legacy-script.sh",
71-
"INIT_SCRIPT": "sleep infinity",
72-
"INIT_COMMAND": "/bin/sh",
72+
"INIT_SCRIPT": "./init-legacy-script.sh",
73+
"INIT_COMMAND": "/bin/zsh",
7374
"INIT_ARGS": "arg1 arg2",
7475
"CACHE_REPO": "example-cache-repo",
7576
"BASE_IMAGE_CACHE_DIR": "/path/to/base/image/cache",
@@ -104,36 +105,36 @@ func TestLegacyEnvVars(t *testing.T) {
104105

105106
o := runCLI()
106107

107-
require.Equal(t, o.SetupScript, legacyEnvs["SETUP_SCRIPT"])
108-
require.Equal(t, o.InitScript, legacyEnvs["INIT_SCRIPT"])
109-
require.Equal(t, o.InitCommand, legacyEnvs["INIT_COMMAND"])
110-
require.Equal(t, o.InitArgs, legacyEnvs["INIT_ARGS"])
111-
require.Equal(t, o.CacheRepo, legacyEnvs["CACHE_REPO"])
112-
require.Equal(t, o.BaseImageCacheDir, legacyEnvs["BASE_IMAGE_CACHE_DIR"])
113-
require.Equal(t, o.LayerCacheDir, legacyEnvs["LAYER_CACHE_DIR"])
114-
require.Equal(t, o.DevcontainerDir, legacyEnvs["DEVCONTAINER_DIR"])
115-
require.Equal(t, o.DevcontainerJSONPath, legacyEnvs["DEVCONTAINER_JSON_PATH"])
116-
require.Equal(t, o.DockerfilePath, legacyEnvs["DOCKERFILE_PATH"])
117-
require.Equal(t, o.BuildContextPath, legacyEnvs["BUILD_CONTEXT_PATH"])
118-
require.Equal(t, o.CacheTTLDays, int64(7))
119-
require.Equal(t, o.DockerConfigBase64, legacyEnvs["DOCKER_CONFIG_BASE64"])
120-
require.Equal(t, o.FallbackImage, legacyEnvs["FALLBACK_IMAGE"])
121-
require.Equal(t, o.ExitOnBuildFailure, true)
122-
require.Equal(t, o.ForceSafe, true)
123-
require.Equal(t, o.Insecure, true)
124-
require.Equal(t, o.IgnorePaths, []string{"/var/run", "/tmp"})
125-
require.Equal(t, o.SkipRebuild, true)
126-
require.Equal(t, o.GitURL, legacyEnvs["GIT_URL"])
127-
require.Equal(t, o.GitCloneDepth, int64(1))
128-
require.Equal(t, o.GitCloneSingleBranch, true)
129-
require.Equal(t, o.GitUsername, legacyEnvs["GIT_USERNAME"])
130-
require.Equal(t, o.GitPassword, legacyEnvs["GIT_PASSWORD"])
131-
require.Equal(t, o.GitSSHPrivateKeyPath, legacyEnvs["GIT_SSH_PRIVATE_KEY_PATH"])
132-
require.Equal(t, o.GitHTTPProxyURL, legacyEnvs["GIT_HTTP_PROXY_URL"])
133-
require.Equal(t, o.WorkspaceFolder, legacyEnvs["WORKSPACE_FOLDER"])
134-
require.Equal(t, o.SSLCertBase64, legacyEnvs["SSL_CERT_BASE64"])
135-
require.Equal(t, o.ExportEnvFile, legacyEnvs["EXPORT_ENV_FILE"])
136-
require.Equal(t, o.PostStartScriptPath, legacyEnvs["POST_START_SCRIPT_PATH"])
108+
assert.Equal(t, legacyEnvs["SETUP_SCRIPT"], o.SetupScript)
109+
assert.Equal(t, legacyEnvs["INIT_SCRIPT"], o.InitScript)
110+
assert.Equal(t, legacyEnvs["INIT_COMMAND"], o.InitCommand)
111+
assert.Equal(t, legacyEnvs["INIT_ARGS"], o.InitArgs)
112+
assert.Equal(t, legacyEnvs["CACHE_REPO"], o.CacheRepo)
113+
assert.Equal(t, legacyEnvs["BASE_IMAGE_CACHE_DIR"], o.BaseImageCacheDir)
114+
assert.Equal(t, legacyEnvs["LAYER_CACHE_DIR"], o.LayerCacheDir)
115+
assert.Equal(t, legacyEnvs["DEVCONTAINER_DIR"], o.DevcontainerDir)
116+
assert.Equal(t, legacyEnvs["DEVCONTAINER_JSON_PATH"], o.DevcontainerJSONPath)
117+
assert.Equal(t, legacyEnvs["DOCKERFILE_PATH"], o.DockerfilePath)
118+
assert.Equal(t, legacyEnvs["BUILD_CONTEXT_PATH"], o.BuildContextPath)
119+
assert.Equal(t, int64(7), o.CacheTTLDays)
120+
assert.Equal(t, legacyEnvs["DOCKER_CONFIG_BASE64"], o.DockerConfigBase64)
121+
assert.Equal(t, legacyEnvs["FALLBACK_IMAGE"], o.FallbackImage)
122+
assert.Equal(t, true, o.ExitOnBuildFailure)
123+
assert.Equal(t, true, o.ForceSafe)
124+
assert.Equal(t, true, o.Insecure)
125+
assert.Equal(t, []string{"/var/run", "/tmp"}, o.IgnorePaths)
126+
assert.Equal(t, true, o.SkipRebuild)
127+
assert.Equal(t, legacyEnvs["GIT_URL"], o.GitURL)
128+
assert.Equal(t, int64(1), o.GitCloneDepth)
129+
assert.Equal(t, true, o.GitCloneSingleBranch)
130+
assert.Equal(t, legacyEnvs["GIT_USERNAME"], o.GitUsername)
131+
assert.Equal(t, legacyEnvs["GIT_PASSWORD"], o.GitPassword)
132+
assert.Equal(t, legacyEnvs["GIT_SSH_PRIVATE_KEY_PATH"], o.GitSSHPrivateKeyPath)
133+
assert.Equal(t, legacyEnvs["GIT_HTTP_PROXY_URL"], o.GitHTTPProxyURL)
134+
assert.Equal(t, legacyEnvs["WORKSPACE_FOLDER"], o.WorkspaceFolder)
135+
assert.Equal(t, legacyEnvs["SSL_CERT_BASE64"], o.SSLCertBase64)
136+
assert.Equal(t, legacyEnvs["EXPORT_ENV_FILE"], o.ExportEnvFile)
137+
assert.Equal(t, legacyEnvs["POST_START_SCRIPT_PATH"], o.PostStartScriptPath)
137138
}
138139

139140
// UpdateGoldenFiles indicates golden files should be updated.

testdata/options.golden

+5-4
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,12 @@ OPTIONS:
107107
The arguments to pass to the init command. They are split according to
108108
/bin/sh rules with https://github.com/kballard/go-shellquote.
109109

110-
--init-command string, $ENVBUILDER_INIT_COMMAND (default: /bin/sh)
111-
The command to run to initialize the workspace.
110+
--init-command string, $ENVBUILDER_INIT_COMMAND
111+
The command to run to initialize the workspace. Default: `/bin/sh`.
112112

113-
--init-script string, $ENVBUILDER_INIT_SCRIPT (default: sleep infinity)
114-
The script to run to initialize the workspace.
113+
--init-script string, $ENVBUILDER_INIT_SCRIPT
114+
The script to run to initialize the workspace. Default: `sleep
115+
infinity`.
115116

116117
--insecure bool, $ENVBUILDER_INSECURE
117118
Bypass TLS verification when cloning and pulling from container

0 commit comments

Comments
 (0)