Skip to content
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