Skip to content

fix: set MagicDir to tempdir when performing cache probe #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 1 addition & 2 deletions internal/imgutil/imgutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"os"
"path/filepath"

"github.com/coder/envbuilder/constants"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
Expand All @@ -34,7 +33,7 @@ func GetRemoteImage(imgRef string) (v1.Image, error) {
// ExtractEnvbuilderFromImage reads the image located at imgRef and extracts
// MagicBinaryLocation to destPath.
func ExtractEnvbuilderFromImage(ctx context.Context, imgRef, destPath string) error {
needle := filepath.Clean(constants.MagicBinaryLocation)[1:] // skip leading '/'
needle := ".envbuilder/bin/envbuilder"
img, err := GetRemoteImage(imgRef)
if err != nil {
return fmt.Errorf("check remote image: %w", err)
Expand Down
3 changes: 2 additions & 1 deletion internal/provider/cached_image_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@
}()

oldKanikoDir := kconfig.KanikoDir
tmpKanikoDir := filepath.Join(tmpDir, constants.MagicDir)
tmpKanikoDir := filepath.Join(tmpDir, ".envbuilder")
// Normally you would set the KANIKO_DIR environment variable, but we are importing kaniko directly.
kconfig.KanikoDir = tmpKanikoDir
tflog.Info(ctx, "set kaniko dir to "+tmpKanikoDir)
Expand All @@ -467,6 +467,7 @@
if err := os.MkdirAll(tmpKanikoDir, 0o755); err != nil {
return nil, fmt.Errorf("failed to create kaniko dir: %w", err)
}
opts.MagicDir = constants.MagicDir(tmpKanikoDir)

Check failure on line 470 in internal/provider/cached_image_resource.go

View workflow job for this annotation

GitHub Actions / generate

opts.MagicDir undefined (type "github.com/coder/envbuilder/options".Options has no field or method MagicDir)

Check failure on line 470 in internal/provider/cached_image_resource.go

View workflow job for this annotation

GitHub Actions / generate

invalid operation: cannot call non-function constants.MagicDir (untyped string constant "/.envbuilder")

Check failure on line 470 in internal/provider/cached_image_resource.go

View workflow job for this annotation

GitHub Actions / Build

opts.MagicDir undefined (type "github.com/coder/envbuilder/options".Options has no field or method MagicDir)

Check failure on line 470 in internal/provider/cached_image_resource.go

View workflow job for this annotation

GitHub Actions / Build

invalid operation: cannot call non-function constants.MagicDir (untyped string constant "/.envbuilder")

Check failure on line 470 in internal/provider/cached_image_resource.go

View workflow job for this annotation

GitHub Actions / generate

opts.MagicDir undefined (type "github.com/coder/envbuilder/options".Options has no field or method MagicDir)

Check failure on line 470 in internal/provider/cached_image_resource.go

View workflow job for this annotation

GitHub Actions / generate

invalid operation: cannot call non-function constants.MagicDir (untyped string constant "/.envbuilder")

Check failure on line 470 in internal/provider/cached_image_resource.go

View workflow job for this annotation

GitHub Actions / Build

opts.MagicDir undefined (type "github.com/coder/envbuilder/options".Options has no field or method MagicDir)

Check failure on line 470 in internal/provider/cached_image_resource.go

View workflow job for this annotation

GitHub Actions / Build

invalid operation: cannot call non-function constants.MagicDir (untyped string constant "/.envbuilder")

// In order to correctly reproduce the final layer of the cached image, we
// need the envbuilder binary used to originally build the image!
Expand Down
9 changes: 6 additions & 3 deletions internal/provider/cached_image_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func TestAccCachedImageResource(t *testing.T) {
"CODER_AGENT_TOKEN", "some-token",
"CODER_AGENT_URL", "https://coder.example.com",
"ENVBUILDER_CACHE_REPO", deps.CacheRepo,
"ENVBUILDER_DOCKER_CONFIG_BASE64", deps.DockerConfigBase64,
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key,
"ENVBUILDER_GIT_URL", deps.Repo.URL,
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
Expand Down Expand Up @@ -78,6 +79,7 @@ RUN date > /date.txt`,
"CODER_AGENT_TOKEN", "some-token",
"CODER_AGENT_URL", "https://coder.example.com",
"ENVBUILDER_CACHE_REPO", deps.CacheRepo,
"ENVBUILDER_DOCKER_CONFIG_BASE64", deps.DockerConfigBase64,
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key,
"ENVBUILDER_GIT_URL", deps.Repo.URL,
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
Expand All @@ -88,9 +90,8 @@ RUN date > /date.txt`,
},
},
{
// This test case ensures that parameters passed via extra_env are
// handled correctly.
name: "extra_env",
// This test case ensures that overriding the devcontainer directory works.
name: "different_dir",
files: map[string]string{
"path/to/.devcontainer/devcontainer.json": `{"build": { "dockerfile": "Dockerfile" }}`,
"path/to/.devcontainer/Dockerfile": `FROM localhost:5000/test-ubuntu:latest
Expand All @@ -115,6 +116,7 @@ RUN date > /date.txt`,
"ENVBUILDER_DEVCONTAINER_DIR", "path/to/.devcontainer",
"ENVBUILDER_DEVCONTAINER_JSON_PATH", "path/to/.devcontainer/devcontainer.json",
"ENVBUILDER_DOCKERFILE_PATH", "path/to/.devcontainer/Dockerfile",
"ENVBUILDER_DOCKER_CONFIG_BASE64", deps.DockerConfigBase64,
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key,
"ENVBUILDER_GIT_URL", deps.Repo.URL,
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
Expand Down Expand Up @@ -149,6 +151,7 @@ RUN date > /date.txt`,
"CODER_AGENT_URL", "https://coder.example.com",
"ENVBUILDER_CACHE_REPO", deps.CacheRepo,
"ENVBUILDER_DOCKERFILE_PATH", "Dockerfile",
"ENVBUILDER_DOCKER_CONFIG_BASE64", deps.DockerConfigBase64,
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key,
"ENVBUILDER_GIT_URL", deps.Repo.URL,
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
Expand Down
38 changes: 26 additions & 12 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package provider
import (
"bufio"
"context"
"encoding/base64"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -35,10 +36,11 @@ var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServe

// testDependencies contain information about stuff the test depends on.
type testDependencies struct {
BuilderImage string
CacheRepo string
ExtraEnv map[string]string
Repo testGitRepoSSH
BuilderImage string
CacheRepo string
DockerConfigBase64 string
ExtraEnv map[string]string
Repo testGitRepoSSH
}

// Config generates a valid Terraform config file from the dependencies.
Expand All @@ -47,8 +49,9 @@ func (d *testDependencies) Config(t testing.TB) string {

tpl := `provider envbuilder {}
resource "envbuilder_cached_image" "test" {
builder_image = {{ quote .BuilderImage }}
builder_image = {{ quote .BuilderImage }}
cache_repo = {{ quote .CacheRepo }}
docker_config_base64 = {{ quote .DockerConfigBase64 }}
git_url = {{ quote .Repo.URL }}
extra_env = {
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH": {{ quote .Repo.Key }}
Expand Down Expand Up @@ -78,19 +81,29 @@ func setup(ctx context.Context, t testing.TB, extraEnv, files map[string]string)
envbuilderVersion := getEnvOrDefault("ENVBUILDER_VERSION", "latest")
envbuilderImageRef := envbuilderImage + ":" + envbuilderVersion

// TODO: envbuilder creates /.envbuilder/bin/envbuilder owned by root:root which we are unable to clean up.
// This causes tests to fail.
testUsername := "testuser"
testPassword := "testpassword"
testAuthBase64 := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", testUsername, testPassword)))
regDir := t.TempDir()
reg := registrytest.New(t, regDir)
reg := registrytest.New(t, regDir, registrytest.BasicAuthMW(t, testUsername, testPassword))

repoDir := setupGitRepo(t, files)
gitRepo := serveGitRepoSSH(ctx, t, repoDir)
dockerConfigJSON := fmt.Sprintf(`{
"auths": {
"%s": {
"auth": "%s",
}
}
}`, reg, testAuthBase64)
dockerConfigJSONBase64 := base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON))

return testDependencies{
BuilderImage: envbuilderImageRef,
CacheRepo: reg + "/test",
ExtraEnv: extraEnv,
Repo: gitRepo,
BuilderImage: envbuilderImageRef,
CacheRepo: reg + "/test",
ExtraEnv: extraEnv,
Repo: gitRepo,
DockerConfigBase64: dockerConfigJSONBase64,
}
}

Expand All @@ -115,6 +128,7 @@ func seedCache(ctx context.Context, t testing.TB, deps testDependencies) {
"ENVBUILDER_VERBOSE": "true",
"ENVBUILDER_GIT_URL": deps.Repo.URL,
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH": "/id_ed25519",
"ENVBUILDER_DOCKER_CONFIG_BASE64": deps.DockerConfigBase64,
}

for k, v := range deps.ExtraEnv {
Expand Down
22 changes: 21 additions & 1 deletion testutil/registrytest/registrytest.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package registrytest

import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
Expand All @@ -13,12 +14,31 @@ import (
// New starts a new Docker registry listening on localhost.
// It will automatically shut down when the test finishes.
// It will store data in dir.
func New(t testing.TB, dir string) string {
func New(t testing.TB, dir string, mws ...func(http.Handler) http.Handler) string {
t.Helper()
regHandler := registry.New(registry.WithBlobHandler(registry.NewDiskBlobHandler(dir)))
for _, mw := range mws {
regHandler = mw(regHandler)
}
regSrv := httptest.NewServer(regHandler)
t.Cleanup(func() { regSrv.Close() })
regSrvURL, err := url.Parse(regSrv.URL)
require.NoError(t, err)
return fmt.Sprintf("localhost:%s", regSrvURL.Port())
}

func BasicAuthMW(t testing.TB, username, password string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if username != "" || password != "" {
authUser, authPass, ok := r.BasicAuth()
if !ok || username != authUser || password != authPass {
t.Logf("basic auth failed: got user %q, pass %q", authUser, authPass)
w.WriteHeader(http.StatusUnauthorized)
return
}
}
next.ServeHTTP(w, r)
})
}
}
Loading