Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e6283db

Browse files
committedAug 15, 2024··
fix(envbuilder): RunCacheProbe: remove references to constants.MagicDir (#315)
(cherry picked from commit be15d1a)
1 parent 59f0fae commit e6283db

File tree

3 files changed

+225
-117
lines changed

3 files changed

+225
-117
lines changed
 

‎envbuilder.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ func RunCacheProbe(ctx context.Context, opts options.Options) (v1.Image, error)
948948
}
949949

950950
defaultBuildParams := func() (*devcontainer.Compiled, error) {
951-
dockerfile := filepath.Join(constants.MagicDir, "Dockerfile")
951+
dockerfile := filepath.Join(buildTimeWorkspaceFolder, "Dockerfile")
952952
file, err := opts.Filesystem.OpenFile(dockerfile, os.O_CREATE|os.O_WRONLY, 0o644)
953953
if err != nil {
954954
return nil, err
@@ -970,7 +970,7 @@ func RunCacheProbe(ctx context.Context, opts options.Options) (v1.Image, error)
970970
return &devcontainer.Compiled{
971971
DockerfilePath: dockerfile,
972972
DockerfileContent: content,
973-
BuildContext: constants.MagicDir,
973+
BuildContext: buildTimeWorkspaceFolder,
974974
}, nil
975975
}
976976

@@ -1010,7 +1010,7 @@ func RunCacheProbe(ctx context.Context, opts options.Options) (v1.Image, error)
10101010
opts.Logger(log.LevelInfo, "No Dockerfile or image specified; falling back to the default image...")
10111011
fallbackDockerfile = defaultParams.DockerfilePath
10121012
}
1013-
buildParams, err = devContainer.Compile(opts.Filesystem, devcontainerDir, constants.MagicDir, fallbackDockerfile, opts.WorkspaceFolder, false, os.LookupEnv)
1013+
buildParams, err = devContainer.Compile(opts.Filesystem, devcontainerDir, buildTimeWorkspaceFolder, fallbackDockerfile, opts.WorkspaceFolder, false, os.LookupEnv)
10141014
if err != nil {
10151015
return nil, fmt.Errorf("compile devcontainer.json: %w", err)
10161016
}

‎integration/integration_test.go

Lines changed: 192 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import (
3838
"github.com/docker/docker/api/types/volume"
3939
"github.com/docker/docker/client"
4040
"github.com/docker/docker/pkg/stdcopy"
41-
"github.com/go-git/go-billy/v5/memfs"
4241
"github.com/google/go-containerregistry/pkg/authn"
4342
"github.com/google/go-containerregistry/pkg/name"
4443
"github.com/google/go-containerregistry/pkg/registry"
@@ -68,8 +67,8 @@ func TestInitScriptInitCommand(t *testing.T) {
6867
w.WriteHeader(http.StatusOK)
6968
}))
7069

71-
srv := createGitServer(t, gitServerOptions{
72-
files: map[string]string{
70+
srv := gittest.CreateGitServer(t, gittest.Options{
71+
Files: map[string]string{
7372
// Let's say /bin/sh is not available and we can only use /bin/ash
7473
"Dockerfile": fmt.Sprintf("FROM %s\nRUN unlink /bin/sh", testImageAlpine),
7574
},
@@ -113,7 +112,7 @@ RUN mkdir -p /myapp/somedir \
113112
&& touch /myapp/somedir/somefile \
114113
&& chown 123:123 /myapp/somedir \
115114
&& chown 321:321 /myapp/somedir/somefile
116-
115+
117116
FROM %s
118117
COPY --from=builder /myapp /myapp
119118
RUN printf "%%s\n" \
@@ -127,8 +126,8 @@ RUN printf "%%s\n" \
127126
/myapp/somedir/somefile \
128127
> /tmp/got \
129128
&& diff -u /tmp/got /tmp/expected`, testImageAlpine, testImageAlpine)
130-
srv := createGitServer(t, gitServerOptions{
131-
files: map[string]string{
129+
srv := gittest.CreateGitServer(t, gittest.Options{
130+
Files: map[string]string{
132131
"Dockerfile": dockerFile,
133132
},
134133
})
@@ -158,8 +157,8 @@ RUN mkdir -p /myapp/somedir \
158157
/myapp/somedir/somefile \
159158
> /tmp/got \
160159
&& diff -u /tmp/got /tmp/expected`, testImageAlpine)
161-
srv := createGitServer(t, gitServerOptions{
162-
files: map[string]string{
160+
srv := gittest.CreateGitServer(t, gittest.Options{
161+
Files: map[string]string{
163162
"Dockerfile": dockerFile,
164163
},
165164
})
@@ -176,8 +175,8 @@ func TestForceSafe(t *testing.T) {
176175

177176
t.Run("Safe", func(t *testing.T) {
178177
t.Parallel()
179-
srv := createGitServer(t, gitServerOptions{
180-
files: map[string]string{
178+
srv := gittest.CreateGitServer(t, gittest.Options{
179+
Files: map[string]string{
181180
"Dockerfile": "FROM " + testImageAlpine,
182181
},
183182
})
@@ -192,8 +191,8 @@ func TestForceSafe(t *testing.T) {
192191
// Careful with this one!
193192
t.Run("Unsafe", func(t *testing.T) {
194193
t.Parallel()
195-
srv := createGitServer(t, gitServerOptions{
196-
files: map[string]string{
194+
srv := gittest.CreateGitServer(t, gittest.Options{
195+
Files: map[string]string{
197196
"Dockerfile": "FROM " + testImageAlpine,
198197
},
199198
})
@@ -209,12 +208,12 @@ func TestForceSafe(t *testing.T) {
209208

210209
func TestFailsGitAuth(t *testing.T) {
211210
t.Parallel()
212-
srv := createGitServer(t, gitServerOptions{
213-
files: map[string]string{
211+
srv := gittest.CreateGitServer(t, gittest.Options{
212+
Files: map[string]string{
214213
"Dockerfile": "FROM " + testImageAlpine,
215214
},
216-
username: "kyle",
217-
password: "testing",
215+
Username: "kyle",
216+
Password: "testing",
218217
})
219218
_, err := runEnvbuilder(t, runOpts{env: []string{
220219
envbuilderEnv("GIT_URL", srv.URL),
@@ -224,12 +223,12 @@ func TestFailsGitAuth(t *testing.T) {
224223

225224
func TestSucceedsGitAuth(t *testing.T) {
226225
t.Parallel()
227-
srv := createGitServer(t, gitServerOptions{
228-
files: map[string]string{
226+
srv := gittest.CreateGitServer(t, gittest.Options{
227+
Files: map[string]string{
229228
"Dockerfile": "FROM " + testImageAlpine,
230229
},
231-
username: "kyle",
232-
password: "testing",
230+
Username: "kyle",
231+
Password: "testing",
233232
})
234233
ctr, err := runEnvbuilder(t, runOpts{env: []string{
235234
envbuilderEnv("GIT_URL", srv.URL),
@@ -244,12 +243,12 @@ func TestSucceedsGitAuth(t *testing.T) {
244243

245244
func TestSucceedsGitAuthInURL(t *testing.T) {
246245
t.Parallel()
247-
srv := createGitServer(t, gitServerOptions{
248-
files: map[string]string{
246+
srv := gittest.CreateGitServer(t, gittest.Options{
247+
Files: map[string]string{
249248
"Dockerfile": "FROM " + testImageAlpine,
250249
},
251-
username: "kyle",
252-
password: "testing",
250+
Username: "kyle",
251+
Password: "testing",
253252
})
254253

255254
u, err := url.Parse(srv.URL)
@@ -309,8 +308,8 @@ func TestBuildFromDevcontainerWithFeatures(t *testing.T) {
309308
require.NoError(t, err)
310309

311310
// Ensures that a Git repository with a devcontainer.json is cloned and built.
312-
srv := createGitServer(t, gitServerOptions{
313-
files: map[string]string{
311+
srv := gittest.CreateGitServer(t, gittest.Options{
312+
Files: map[string]string{
314313
".devcontainer/devcontainer.json": `{
315314
"name": "Test",
316315
"build": {
@@ -350,8 +349,8 @@ func TestBuildFromDevcontainerWithFeatures(t *testing.T) {
350349

351350
func TestBuildFromDockerfile(t *testing.T) {
352351
// Ensures that a Git repository with a Dockerfile is cloned and built.
353-
srv := createGitServer(t, gitServerOptions{
354-
files: map[string]string{
352+
srv := gittest.CreateGitServer(t, gittest.Options{
353+
Files: map[string]string{
355354
"Dockerfile": "FROM " + testImageAlpine,
356355
},
357356
})
@@ -372,8 +371,8 @@ func TestBuildFromDockerfile(t *testing.T) {
372371

373372
func TestBuildPrintBuildOutput(t *testing.T) {
374373
// Ensures that a Git repository with a Dockerfile is cloned and built.
375-
srv := createGitServer(t, gitServerOptions{
376-
files: map[string]string{
374+
srv := gittest.CreateGitServer(t, gittest.Options{
375+
Files: map[string]string{
377376
"Dockerfile": "FROM " + testImageAlpine + "\nRUN echo hello",
378377
},
379378
})
@@ -400,8 +399,8 @@ func TestBuildPrintBuildOutput(t *testing.T) {
400399

401400
func TestBuildIgnoreVarRunSecrets(t *testing.T) {
402401
// Ensures that a Git repository with a Dockerfile is cloned and built.
403-
srv := createGitServer(t, gitServerOptions{
404-
files: map[string]string{
402+
srv := gittest.CreateGitServer(t, gittest.Options{
403+
Files: map[string]string{
405404
"Dockerfile": "FROM " + testImageAlpine,
406405
},
407406
})
@@ -441,8 +440,8 @@ func TestBuildIgnoreVarRunSecrets(t *testing.T) {
441440

442441
func TestBuildWithSetupScript(t *testing.T) {
443442
// Ensures that a Git repository with a Dockerfile is cloned and built.
444-
srv := createGitServer(t, gitServerOptions{
445-
files: map[string]string{
443+
srv := gittest.CreateGitServer(t, gittest.Options{
444+
Files: map[string]string{
446445
"Dockerfile": "FROM " + testImageAlpine,
447446
},
448447
})
@@ -461,8 +460,8 @@ func TestBuildFromDevcontainerInCustomPath(t *testing.T) {
461460
t.Parallel()
462461

463462
// Ensures that a Git repository with a devcontainer.json is cloned and built.
464-
srv := createGitServer(t, gitServerOptions{
465-
files: map[string]string{
463+
srv := gittest.CreateGitServer(t, gittest.Options{
464+
Files: map[string]string{
466465
".devcontainer/custom/devcontainer.json": `{
467466
"name": "Test",
468467
"build": {
@@ -486,8 +485,8 @@ func TestBuildFromDevcontainerInSubfolder(t *testing.T) {
486485
t.Parallel()
487486

488487
// Ensures that a Git repository with a devcontainer.json is cloned and built.
489-
srv := createGitServer(t, gitServerOptions{
490-
files: map[string]string{
488+
srv := gittest.CreateGitServer(t, gittest.Options{
489+
Files: map[string]string{
491490
".devcontainer/subfolder/devcontainer.json": `{
492491
"name": "Test",
493492
"build": {
@@ -510,8 +509,8 @@ func TestBuildFromDevcontainerInRoot(t *testing.T) {
510509
t.Parallel()
511510

512511
// Ensures that a Git repository with a devcontainer.json is cloned and built.
513-
srv := createGitServer(t, gitServerOptions{
514-
files: map[string]string{
512+
srv := gittest.CreateGitServer(t, gittest.Options{
513+
Files: map[string]string{
515514
"devcontainer.json": `{
516515
"name": "Test",
517516
"build": {
@@ -531,11 +530,11 @@ func TestBuildFromDevcontainerInRoot(t *testing.T) {
531530
}
532531

533532
func TestBuildCustomCertificates(t *testing.T) {
534-
srv := createGitServer(t, gitServerOptions{
535-
files: map[string]string{
533+
srv := gittest.CreateGitServer(t, gittest.Options{
534+
Files: map[string]string{
536535
"Dockerfile": "FROM " + testImageAlpine,
537536
},
538-
tls: true,
537+
TLS: true,
539538
})
540539
ctr, err := runEnvbuilder(t, runOpts{env: []string{
541540
envbuilderEnv("GIT_URL", srv.URL),
@@ -553,8 +552,8 @@ func TestBuildCustomCertificates(t *testing.T) {
553552

554553
func TestBuildStopStartCached(t *testing.T) {
555554
// Ensures that a Git repository with a Dockerfile is cloned and built.
556-
srv := createGitServer(t, gitServerOptions{
557-
files: map[string]string{
555+
srv := gittest.CreateGitServer(t, gittest.Options{
556+
Files: map[string]string{
558557
"Dockerfile": "FROM " + testImageAlpine,
559558
},
560559
})
@@ -601,8 +600,8 @@ func TestBuildFailsFallback(t *testing.T) {
601600
t.Run("BadDockerfile", func(t *testing.T) {
602601
t.Parallel()
603602
// Ensures that a Git repository with a Dockerfile is cloned and built.
604-
srv := createGitServer(t, gitServerOptions{
605-
files: map[string]string{
603+
srv := gittest.CreateGitServer(t, gittest.Options{
604+
Files: map[string]string{
606605
"Dockerfile": "bad syntax",
607606
},
608607
})
@@ -616,8 +615,8 @@ func TestBuildFailsFallback(t *testing.T) {
616615
t.Run("FailsBuild", func(t *testing.T) {
617616
t.Parallel()
618617
// Ensures that a Git repository with a Dockerfile is cloned and built.
619-
srv := createGitServer(t, gitServerOptions{
620-
files: map[string]string{
618+
srv := gittest.CreateGitServer(t, gittest.Options{
619+
Files: map[string]string{
621620
"Dockerfile": `FROM ` + testImageAlpine + `
622621
RUN exit 1`,
623622
},
@@ -631,8 +630,8 @@ RUN exit 1`,
631630
t.Run("BadDevcontainer", func(t *testing.T) {
632631
t.Parallel()
633632
// Ensures that a Git repository with a Dockerfile is cloned and built.
634-
srv := createGitServer(t, gitServerOptions{
635-
files: map[string]string{
633+
srv := gittest.CreateGitServer(t, gittest.Options{
634+
Files: map[string]string{
636635
".devcontainer/devcontainer.json": "not json",
637636
},
638637
})
@@ -643,8 +642,8 @@ RUN exit 1`,
643642
})
644643
t.Run("NoImageOrDockerfile", func(t *testing.T) {
645644
t.Parallel()
646-
srv := createGitServer(t, gitServerOptions{
647-
files: map[string]string{
645+
srv := gittest.CreateGitServer(t, gittest.Options{
646+
Files: map[string]string{
648647
".devcontainer/devcontainer.json": "{}",
649648
},
650649
})
@@ -661,8 +660,8 @@ RUN exit 1`,
661660

662661
func TestExitBuildOnFailure(t *testing.T) {
663662
t.Parallel()
664-
srv := createGitServer(t, gitServerOptions{
665-
files: map[string]string{
663+
srv := gittest.CreateGitServer(t, gittest.Options{
664+
Files: map[string]string{
666665
"Dockerfile": "bad syntax",
667666
},
668667
})
@@ -680,8 +679,8 @@ func TestContainerEnv(t *testing.T) {
680679
t.Parallel()
681680

682681
// Ensures that a Git repository with a devcontainer.json is cloned and built.
683-
srv := createGitServer(t, gitServerOptions{
684-
files: map[string]string{
682+
srv := gittest.CreateGitServer(t, gittest.Options{
683+
Files: map[string]string{
685684
".devcontainer/devcontainer.json": `{
686685
"name": "Test",
687686
"build": {
@@ -722,8 +721,8 @@ func TestUnsetOptionsEnv(t *testing.T) {
722721
t.Parallel()
723722

724723
// Ensures that a Git repository with a devcontainer.json is cloned and built.
725-
srv := createGitServer(t, gitServerOptions{
726-
files: map[string]string{
724+
srv := gittest.CreateGitServer(t, gittest.Options{
725+
Files: map[string]string{
727726
".devcontainer/devcontainer.json": `{
728727
"name": "Test",
729728
"build": {
@@ -762,8 +761,8 @@ func TestLifecycleScripts(t *testing.T) {
762761
t.Parallel()
763762

764763
// Ensures that a Git repository with a devcontainer.json is cloned and built.
765-
srv := createGitServer(t, gitServerOptions{
766-
files: map[string]string{
764+
srv := gittest.CreateGitServer(t, gittest.Options{
765+
Files: map[string]string{
767766
".devcontainer/devcontainer.json": `{
768767
"name": "Test",
769768
"build": {
@@ -798,8 +797,8 @@ func TestPostStartScript(t *testing.T) {
798797
t.Parallel()
799798

800799
// Ensures that a Git repository with a devcontainer.json is cloned and built.
801-
srv := createGitServer(t, gitServerOptions{
802-
files: map[string]string{
800+
srv := gittest.CreateGitServer(t, gittest.Options{
801+
Files: map[string]string{
803802
".devcontainer/devcontainer.json": `{
804803
"name": "Test",
805804
"build": {
@@ -848,8 +847,8 @@ func TestPrivateRegistry(t *testing.T) {
848847
})
849848

850849
// Ensures that a Git repository with a Dockerfile is cloned and built.
851-
srv := createGitServer(t, gitServerOptions{
852-
files: map[string]string{
850+
srv := gittest.CreateGitServer(t, gittest.Options{
851+
Files: map[string]string{
853852
"Dockerfile": "FROM " + image,
854853
},
855854
})
@@ -867,8 +866,8 @@ func TestPrivateRegistry(t *testing.T) {
867866
})
868867

869868
// Ensures that a Git repository with a Dockerfile is cloned and built.
870-
srv := createGitServer(t, gitServerOptions{
871-
files: map[string]string{
869+
srv := gittest.CreateGitServer(t, gittest.Options{
870+
Files: map[string]string{
872871
"Dockerfile": "FROM " + image,
873872
},
874873
})
@@ -899,8 +898,8 @@ func TestPrivateRegistry(t *testing.T) {
899898
})
900899

901900
// Ensures that a Git repository with a Dockerfile is cloned and built.
902-
srv := createGitServer(t, gitServerOptions{
903-
files: map[string]string{
901+
srv := gittest.CreateGitServer(t, gittest.Options{
902+
Files: map[string]string{
904903
"Dockerfile": "FROM " + image,
905904
},
906905
})
@@ -1042,8 +1041,8 @@ COPY %s .`, testImageAlpine, inclFile)
10421041
tc := tc
10431042

10441043
t.Run(tc.name, func(t *testing.T) {
1045-
srv := createGitServer(t, gitServerOptions{
1046-
files: tc.files,
1044+
srv := gittest.CreateGitServer(t, gittest.Options{
1045+
Files: tc.files,
10471046
})
10481047
_, err := runEnvbuilder(t, runOpts{env: []string{
10491048
envbuilderEnv("GIT_URL", srv.URL),
@@ -1066,8 +1065,8 @@ func TestPushImage(t *testing.T) {
10661065
t.Run("CacheWithoutPush", func(t *testing.T) {
10671066
t.Parallel()
10681067

1069-
srv := createGitServer(t, gitServerOptions{
1070-
files: map[string]string{
1068+
srv := gittest.CreateGitServer(t, gittest.Options{
1069+
Files: map[string]string{
10711070
".devcontainer/Dockerfile": fmt.Sprintf(`FROM %s
10721071
USER root
10731072
ARG WORKDIR=/
@@ -1127,8 +1126,8 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
11271126
t.Run("CacheAndPush", func(t *testing.T) {
11281127
t.Parallel()
11291128

1130-
srv := createGitServer(t, gitServerOptions{
1131-
files: map[string]string{
1129+
srv := gittest.CreateGitServer(t, gittest.Options{
1130+
Files: map[string]string{
11321131
".devcontainer/Dockerfile": fmt.Sprintf(`FROM %s
11331132
USER root
11341133
ARG WORKDIR=/
@@ -1243,11 +1242,117 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
12431242
require.NotEmpty(t, strings.TrimSpace(out))
12441243
})
12451244

1245+
t.Run("CacheAndPushDevcontainerOnly", func(t *testing.T) {
1246+
t.Parallel()
1247+
1248+
srv := gittest.CreateGitServer(t, gittest.Options{
1249+
Files: map[string]string{
1250+
".devcontainer/devcontainer.json": fmt.Sprintf(`{"image": %q}`, testImageAlpine),
1251+
},
1252+
})
1253+
1254+
// Given: an empty registry
1255+
testReg := setupInMemoryRegistry(t, setupInMemoryRegistryOpts{})
1256+
testRepo := testReg + "/test"
1257+
ref, err := name.ParseReference(testRepo + ":latest")
1258+
require.NoError(t, err)
1259+
_, err = remote.Image(ref)
1260+
require.ErrorContains(t, err, "NAME_UNKNOWN", "expected image to not be present before build + push")
1261+
1262+
// When: we run envbuilder with GET_CACHED_IMAGE
1263+
_, err = runEnvbuilder(t, runOpts{env: []string{
1264+
envbuilderEnv("GIT_URL", srv.URL),
1265+
envbuilderEnv("CACHE_REPO", testRepo),
1266+
envbuilderEnv("GET_CACHED_IMAGE", "1"),
1267+
}})
1268+
require.ErrorContains(t, err, "error probing build cache: uncached COPY command")
1269+
// Then: it should fail to build the image and nothing should be pushed
1270+
_, err = remote.Image(ref)
1271+
require.ErrorContains(t, err, "NAME_UNKNOWN", "expected image to not be present before build + push")
1272+
1273+
// When: we run envbuilder with PUSH_IMAGE set
1274+
_, err = runEnvbuilder(t, runOpts{env: []string{
1275+
envbuilderEnv("GIT_URL", srv.URL),
1276+
envbuilderEnv("CACHE_REPO", testRepo),
1277+
envbuilderEnv("PUSH_IMAGE", "1"),
1278+
}})
1279+
require.NoError(t, err)
1280+
1281+
// Then: the image should be pushed
1282+
img, err := remote.Image(ref)
1283+
require.NoError(t, err, "expected image to be present after build + push")
1284+
1285+
// Then: the image should have its directives replaced with those required
1286+
// to run envbuilder automatically
1287+
configFile, err := img.ConfigFile()
1288+
require.NoError(t, err, "expected image to return a config file")
1289+
1290+
assert.Equal(t, "root", configFile.Config.User, "user must be root")
1291+
assert.Equal(t, "/", configFile.Config.WorkingDir, "workdir must be /")
1292+
if assert.Len(t, configFile.Config.Entrypoint, 1) {
1293+
assert.Equal(t, "/.envbuilder/bin/envbuilder", configFile.Config.Entrypoint[0], "incorrect entrypoint")
1294+
}
1295+
1296+
// Then: re-running envbuilder with GET_CACHED_IMAGE should succeed
1297+
ctrID, err := runEnvbuilder(t, runOpts{env: []string{
1298+
envbuilderEnv("GIT_URL", srv.URL),
1299+
envbuilderEnv("CACHE_REPO", testRepo),
1300+
envbuilderEnv("GET_CACHED_IMAGE", "1"),
1301+
}})
1302+
require.NoError(t, err)
1303+
1304+
// Then: the cached image ref should be emitted in the container logs
1305+
ctx, cancel := context.WithCancel(context.Background())
1306+
t.Cleanup(cancel)
1307+
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
1308+
require.NoError(t, err)
1309+
defer cli.Close()
1310+
logs, err := cli.ContainerLogs(ctx, ctrID, container.LogsOptions{
1311+
ShowStdout: true,
1312+
ShowStderr: true,
1313+
})
1314+
require.NoError(t, err)
1315+
defer logs.Close()
1316+
logBytes, err := io.ReadAll(logs)
1317+
require.NoError(t, err)
1318+
require.Regexp(t, `ENVBUILDER_CACHED_IMAGE=(\S+)`, string(logBytes))
1319+
1320+
// When: we pull the image we just built
1321+
rc, err := cli.ImagePull(ctx, ref.String(), image.PullOptions{})
1322+
require.NoError(t, err)
1323+
t.Cleanup(func() { _ = rc.Close() })
1324+
_, err = io.ReadAll(rc)
1325+
require.NoError(t, err)
1326+
1327+
// When: we run the image we just built
1328+
ctr, err := cli.ContainerCreate(ctx, &container.Config{
1329+
Image: ref.String(),
1330+
Entrypoint: []string{"sleep", "infinity"},
1331+
Labels: map[string]string{
1332+
testContainerLabel: "true",
1333+
},
1334+
}, nil, nil, nil, "")
1335+
require.NoError(t, err)
1336+
t.Cleanup(func() {
1337+
_ = cli.ContainerRemove(ctx, ctr.ID, container.RemoveOptions{
1338+
RemoveVolumes: true,
1339+
Force: true,
1340+
})
1341+
})
1342+
err = cli.ContainerStart(ctx, ctr.ID, container.StartOptions{})
1343+
require.NoError(t, err)
1344+
1345+
// Then: the envbuilder binary exists in the image!
1346+
out := execContainer(t, ctr.ID, "/.envbuilder/bin/envbuilder --help")
1347+
require.Regexp(t, `(?s)^USAGE:\s+envbuilder`, strings.TrimSpace(out))
1348+
require.NotEmpty(t, strings.TrimSpace(out))
1349+
})
1350+
12461351
t.Run("CacheAndPushAuth", func(t *testing.T) {
12471352
t.Parallel()
12481353

1249-
srv := createGitServer(t, gitServerOptions{
1250-
files: map[string]string{
1354+
srv := gittest.CreateGitServer(t, gittest.Options{
1355+
Files: map[string]string{
12511356
".devcontainer/Dockerfile": fmt.Sprintf(`FROM %s
12521357
USER root
12531358
ARG WORKDIR=/
@@ -1323,8 +1428,8 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
13231428
t.Run("CacheAndPushAuthFail", func(t *testing.T) {
13241429
t.Parallel()
13251430

1326-
srv := createGitServer(t, gitServerOptions{
1327-
files: map[string]string{
1431+
srv := gittest.CreateGitServer(t, gittest.Options{
1432+
Files: map[string]string{
13281433
".devcontainer/Dockerfile": fmt.Sprintf(`FROM %s
13291434
USER root
13301435
ARG WORKDIR=/
@@ -1390,8 +1495,8 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
13901495
t.Skip("TODO: https://github.com/coder/envbuilder/issues/230")
13911496
t.Parallel()
13921497

1393-
srv := createGitServer(t, gitServerOptions{
1394-
files: map[string]string{
1498+
srv := gittest.CreateGitServer(t, gittest.Options{
1499+
Files: map[string]string{
13951500
"Dockerfile": fmt.Sprintf(`FROM %s AS a
13961501
RUN date --utc > /root/date.txt
13971502
FROM %s as b
@@ -1448,8 +1553,8 @@ COPY --from=a /root/date.txt /date.txt`, testImageAlpine, testImageAlpine),
14481553
t.Run("PushImageRequiresCache", func(t *testing.T) {
14491554
t.Parallel()
14501555

1451-
srv := createGitServer(t, gitServerOptions{
1452-
files: map[string]string{
1556+
srv := gittest.CreateGitServer(t, gittest.Options{
1557+
Files: map[string]string{
14531558
".devcontainer/Dockerfile": fmt.Sprintf(`FROM %s
14541559
USER root
14551560
ARG WORKDIR=/
@@ -1480,8 +1585,8 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
14801585
t.Run("PushErr", func(t *testing.T) {
14811586
t.Parallel()
14821587

1483-
srv := createGitServer(t, gitServerOptions{
1484-
files: map[string]string{
1588+
srv := gittest.CreateGitServer(t, gittest.Options{
1589+
Files: map[string]string{
14851590
".devcontainer/Dockerfile": fmt.Sprintf(`FROM %s
14861591
USER root
14871592
ARG WORKDIR=/
@@ -1518,8 +1623,8 @@ func TestChownHomedir(t *testing.T) {
15181623
t.Parallel()
15191624

15201625
// Ensures that a Git repository with a devcontainer.json is cloned and built.
1521-
srv := createGitServer(t, gitServerOptions{
1522-
files: map[string]string{
1626+
srv := gittest.CreateGitServer(t, gittest.Options{
1627+
Files: map[string]string{
15231628
".devcontainer/devcontainer.json": `{
15241629
"name": "Test",
15251630
"build": {
@@ -1591,33 +1696,6 @@ func TestMain(m *testing.M) {
15911696
m.Run()
15921697
}
15931698

1594-
type gitServerOptions struct {
1595-
files map[string]string
1596-
username string
1597-
password string
1598-
authMW func(http.Handler) http.Handler
1599-
tls bool
1600-
}
1601-
1602-
// createGitServer creates a git repository with an in-memory filesystem
1603-
// and serves it over HTTP using a httptest.Server.
1604-
func createGitServer(t *testing.T, opts gitServerOptions) *httptest.Server {
1605-
t.Helper()
1606-
if opts.authMW == nil {
1607-
opts.authMW = mwtest.BasicAuthMW(opts.username, opts.password)
1608-
}
1609-
commits := make([]gittest.CommitFunc, 0)
1610-
for path, content := range opts.files {
1611-
commits = append(commits, gittest.Commit(t, path, content, "my test commit"))
1612-
}
1613-
fs := memfs.New()
1614-
_ = gittest.NewRepo(t, fs, commits...)
1615-
if opts.tls {
1616-
return httptest.NewTLSServer(opts.authMW(gittest.NewServer(fs)))
1617-
}
1618-
return httptest.NewServer(opts.authMW(gittest.NewServer(fs)))
1619-
}
1620-
16211699
func checkTestRegistry() {
16221700
resp, err := http.Get("http://localhost:5000/v2/_catalog")
16231701
if err != nil {

‎testutil/gittest/gittest.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"log"
77
"net"
88
"net/http"
9+
"net/http/httptest"
910
"os"
1011
"os/exec"
1112
"sync"
@@ -14,8 +15,10 @@ import (
1415

1516
gossh "golang.org/x/crypto/ssh"
1617

18+
"github.com/coder/envbuilder/testutil/mwtest"
1719
"github.com/gliderlabs/ssh"
1820
"github.com/go-git/go-billy/v5"
21+
"github.com/go-git/go-billy/v5/memfs"
1922
"github.com/go-git/go-git/v5"
2023
"github.com/go-git/go-git/v5/plumbing"
2124
"github.com/go-git/go-git/v5/plumbing/cache"
@@ -28,6 +31,33 @@ import (
2831
"github.com/stretchr/testify/require"
2932
)
3033

34+
type Options struct {
35+
Files map[string]string
36+
Username string
37+
Password string
38+
AuthMW func(http.Handler) http.Handler
39+
TLS bool
40+
}
41+
42+
// CreateGitServer creates a git repository with an in-memory filesystem
43+
// and serves it over HTTP using a httptest.Server.
44+
func CreateGitServer(t *testing.T, opts Options) *httptest.Server {
45+
t.Helper()
46+
if opts.AuthMW == nil {
47+
opts.AuthMW = mwtest.BasicAuthMW(opts.Username, opts.Password)
48+
}
49+
commits := make([]CommitFunc, 0)
50+
for path, content := range opts.Files {
51+
commits = append(commits, Commit(t, path, content, "my test commit"))
52+
}
53+
fs := memfs.New()
54+
_ = NewRepo(t, fs, commits...)
55+
if opts.TLS {
56+
return httptest.NewTLSServer(opts.AuthMW(NewServer(fs)))
57+
}
58+
return httptest.NewServer(opts.AuthMW(NewServer(fs)))
59+
}
60+
3161
// NewServer returns a http.Handler that serves a git repository.
3262
// It's expected that the repository is already initialized by the caller.
3363
func NewServer(fs billy.Filesystem) http.Handler {

0 commit comments

Comments
 (0)
Please sign in to comment.