From 8a5abd10bce5a3ababc6930c58d8a65ac0cb7540 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 24 Jun 2024 14:01:33 +0100 Subject: [PATCH 1/2] chore(integration): consolidate unit test --- integration/integration_test.go | 104 +++++++++++--------------------- 1 file changed, 36 insertions(+), 68 deletions(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index aed4adae..4aa9e7d4 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -1148,6 +1148,42 @@ func TestPushImage(t *testing.T) { envbuilderEnv("GET_CACHED_IMAGE", "1"), }}) require.NoError(t, err) + + // When: we pull the image we just built + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + require.NoError(t, err) + defer cli.Close() + rc, err := cli.ImagePull(ctx, ref.String(), image.PullOptions{}) + require.NoError(t, err) + t.Cleanup(func() { _ = rc.Close() }) + _, err = io.ReadAll(rc) + require.NoError(t, err) + + // When: we run the image we just built + ctr, err := cli.ContainerCreate(ctx, &container.Config{ + Image: ref.String(), + Cmd: []string{"sleep", "infinity"}, + Labels: map[string]string{ + testContainerLabel: "true", + }, + }, nil, nil, nil, "") + require.NoError(t, err) + t.Cleanup(func() { + _ = cli.ContainerRemove(ctx, ctr.ID, container.RemoveOptions{ + RemoveVolumes: true, + Force: true, + }) + }) + err = cli.ContainerStart(ctx, ctr.ID, container.StartOptions{}) + require.NoError(t, err) + + // Then: the envbuilder binary exists in the image! + out := execContainer(t, ctr.ID, "[[ -f \"/.envbuilder/bin/envbuilder\" ]] && echo \"exists\"") + require.Equal(t, "exists", strings.TrimSpace(out)) + out = execContainer(t, ctr.ID, "cat /root/date.txt") + require.NotEmpty(t, strings.TrimSpace(out)) }) t.Run("CacheAndPushAuth", func(t *testing.T) { @@ -1397,74 +1433,6 @@ COPY --from=a /root/date.txt /date.txt`, testImageAlpine, testImageAlpine), }) } -func TestEmbedBinaryImage(t *testing.T) { - t.Parallel() - - srv := createGitServer(t, gitServerOptions{ - files: map[string]string{ - ".devcontainer/Dockerfile": fmt.Sprintf("FROM %s\nRUN date --utc > /root/date.txt", testImageAlpine), - ".devcontainer/devcontainer.json": `{ - "name": "Test", - "build": { - "dockerfile": "Dockerfile" - }, - }`, - }, - }) - - testReg := setupInMemoryRegistry(t, setupInMemoryRegistryOpts{}) - testRepo := testReg + "/test-embed-binary-image" - ref, err := name.ParseReference(testRepo + ":latest") - require.NoError(t, err) - - _, err = runEnvbuilder(t, options{env: []string{ - envbuilderEnv("GIT_URL", srv.URL), - envbuilderEnv("CACHE_REPO", testRepo), - envbuilderEnv("PUSH_IMAGE", "1"), - }}) - require.NoError(t, err) - - _, err = remote.Image(ref) - require.NoError(t, err, "expected image to be present after build + push") - - ctx := context.Background() - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - require.NoError(t, err) - t.Cleanup(func() { - cli.Close() - }) - - // Pull the image we just built - rc, err := cli.ImagePull(ctx, ref.String(), image.PullOptions{}) - require.NoError(t, err) - t.Cleanup(func() { _ = rc.Close() }) - _, err = io.ReadAll(rc) - require.NoError(t, err) - - // Run it - ctr, err := cli.ContainerCreate(ctx, &container.Config{ - Image: ref.String(), - Cmd: []string{"sleep", "infinity"}, - Labels: map[string]string{ - testContainerLabel: "true", - }, - }, nil, nil, nil, "") - require.NoError(t, err) - t.Cleanup(func() { - _ = cli.ContainerRemove(ctx, ctr.ID, container.RemoveOptions{ - RemoveVolumes: true, - Force: true, - }) - }) - err = cli.ContainerStart(ctx, ctr.ID, container.StartOptions{}) - require.NoError(t, err) - - out := execContainer(t, ctr.ID, "[[ -f \"/.envbuilder/bin/envbuilder\" ]] && echo \"exists\"") - require.Equal(t, "exists", strings.TrimSpace(out)) - out = execContainer(t, ctr.ID, "cat /root/date.txt") - require.NotEmpty(t, strings.TrimSpace(out)) -} - func TestChownHomedir(t *testing.T) { t.Parallel() From 4deaef6a11dfef4805e17c7db0dd4079ea83f3b1 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 24 Jun 2024 14:54:33 +0100 Subject: [PATCH 2/2] set user, workdir, entrypoint on pushed image --- envbuilder.go | 6 +++++- integration/integration_test.go | 21 ++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/envbuilder.go b/envbuilder.go index 889db1e4..9b5b3cc4 100644 --- a/envbuilder.go +++ b/envbuilder.go @@ -420,7 +420,11 @@ func Run(ctx context.Context, options Options) error { return xerrors.Errorf("add exe path to ignore list: %w", err) } // Copy the envbuilder binary into the build context. - buildParams.DockerfileContent += fmt.Sprintf("\nCOPY %s %s", exePath, exePath) + buildParams.DockerfileContent += fmt.Sprintf(` +COPY --chmod=0755 %s %s +USER root +WORKDIR / +ENTRYPOINT [%q]`, exePath, exePath, exePath) dst := filepath.Join(buildParams.BuildContext, exePath) if err := copyFile(exePath, dst); err != nil { return xerrors.Errorf("copy running binary to build context: %w", err) diff --git a/integration/integration_test.go b/integration/integration_test.go index 4aa9e7d4..02b09063 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -1138,9 +1138,20 @@ func TestPushImage(t *testing.T) { require.NoError(t, err) // Then: the image should be pushed - _, err = remote.Image(ref) + img, err := remote.Image(ref) require.NoError(t, err, "expected image to be present after build + push") + // Then: the image should have its directives replaced with those required + // to run envbuilder automatically + configFile, err := img.ConfigFile() + require.NoError(t, err, "expected image to return a config file") + + assert.Equal(t, "root", configFile.Config.User, "user must be root") + assert.Equal(t, "/", configFile.Config.WorkingDir, "workdir must be /") + if assert.Len(t, configFile.Config.Entrypoint, 1) { + assert.Equal(t, "/.envbuilder/bin/envbuilder", configFile.Config.Entrypoint[0], "incorrect entrypoint") + } + // Then: re-running envbuilder with GET_CACHED_IMAGE should succeed _, err = runEnvbuilder(t, options{env: []string{ envbuilderEnv("GIT_URL", srv.URL), @@ -1163,8 +1174,8 @@ func TestPushImage(t *testing.T) { // When: we run the image we just built ctr, err := cli.ContainerCreate(ctx, &container.Config{ - Image: ref.String(), - Cmd: []string{"sleep", "infinity"}, + Image: ref.String(), + Entrypoint: []string{"sleep", "infinity"}, Labels: map[string]string{ testContainerLabel: "true", }, @@ -1180,8 +1191,8 @@ func TestPushImage(t *testing.T) { require.NoError(t, err) // Then: the envbuilder binary exists in the image! - out := execContainer(t, ctr.ID, "[[ -f \"/.envbuilder/bin/envbuilder\" ]] && echo \"exists\"") - require.Equal(t, "exists", strings.TrimSpace(out)) + out := execContainer(t, ctr.ID, "/.envbuilder/bin/envbuilder --help") + require.Regexp(t, `(?s)^USAGE:\s+envbuilder`, strings.TrimSpace(out)) out = execContainer(t, ctr.ID, "cat /root/date.txt") require.NotEmpty(t, strings.TrimSpace(out)) })