From 4340212ae7c7e00d87cfc41c3119bca622b9b46c Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Sun, 20 Oct 2024 21:46:50 +0000 Subject: [PATCH 1/7] fix: parse image auth config correctly --- cli/docker.go | 10 +++++----- dockerutil/client.go | 38 +++++++++++++++----------------------- dockerutil/client_test.go | 21 +++++++++++++++++++++ 3 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 dockerutil/client_test.go diff --git a/cli/docker.go b/cli/docker.go index a798ee4..8ffbb48 100644 --- a/cli/docker.go +++ b/cli/docker.go @@ -397,10 +397,14 @@ func runDockerCVM(ctx context.Context, log slog.Logger, client dockerutil.Docker if err != nil { return xerrors.Errorf("set oom score: %w", err) } + ref, err := name.NewTag(flags.innerImage) + if err != nil { + return xerrors.Errorf("parse ref: %w", err) + } var dockerAuth dockerutil.AuthConfig if flags.imagePullSecret != "" { - dockerAuth, err = dockerutil.ParseAuthConfig(flags.imagePullSecret) + dockerAuth, err = dockerutil.AuthConfigFromString(flags.imagePullSecret, ref.RegistryStr()) if err != nil { return xerrors.Errorf("parse auth config: %w", err) } @@ -409,10 +413,6 @@ func runDockerCVM(ctx context.Context, log slog.Logger, client dockerutil.Docker log.Info(ctx, "checking for docker config file", slog.F("path", flags.dockerConfig)) if _, err := fs.Stat(flags.dockerConfig); err == nil { log.Info(ctx, "detected file", slog.F("image", flags.innerImage)) - ref, err := name.NewTag(flags.innerImage) - if err != nil { - return xerrors.Errorf("parse ref: %w", err) - } dockerAuth, err = dockerutil.AuthConfigFromPath(flags.dockerConfig, ref.RegistryStr()) if err != nil && !xerrors.Is(err, os.ErrNotExist) { return xerrors.Errorf("auth config from file: %w", err) diff --git a/dockerutil/client.go b/dockerutil/client.go index 7e97f71..e9d1ef2 100644 --- a/dockerutil/client.go +++ b/dockerutil/client.go @@ -48,20 +48,30 @@ func (a AuthConfig) Base64() (string, error) { return base64.URLEncoding.EncodeToString(authStr), nil } -func AuthConfigFromPath(path string, registry string) (AuthConfig, error) { +func AuthConfigFromPath(path string, reg string) (AuthConfig, error) { var config dockercfg.Config err := dockercfg.FromFile(path, &config) if err != nil { return AuthConfig{}, xerrors.Errorf("load config: %w", err) } - hostname := dockercfg.ResolveRegistryHost(registry) + return parseConfig(config, reg) +} - if config, ok := config.AuthConfigs[registry]; ok { - return AuthConfig(config), nil +func AuthConfigFromString(raw string, reg string) (AuthConfig, error) { + var cfg dockercfg.Config + err := json.Unmarshal([]byte(raw), &cfg) + if err != nil { + return AuthConfig{}, xerrors.Errorf("parse config: %w", err) } + return parseConfig(cfg, reg) +} + +func parseConfig(cfg dockercfg.Config, registry string) (AuthConfig, error) { + + hostname := dockercfg.ResolveRegistryHost(registry) - username, secret, err := config.GetRegistryCredentials(hostname) + username, secret, err := cfg.GetRegistryCredentials(hostname) if err != nil { return AuthConfig{}, xerrors.Errorf("get credentials from helper: %w", err) } @@ -79,23 +89,5 @@ func AuthConfigFromPath(path string, registry string) (AuthConfig, error) { } return AuthConfig{}, xerrors.Errorf("no auth config found for registry %s: %w", registry, os.ErrNotExist) -} - -func ParseAuthConfig(raw string) (AuthConfig, error) { - type dockerConfig struct { - AuthConfigs map[string]dockertypes.AuthConfig `json:"auths"` - } - - var conf dockerConfig - if err := json.Unmarshal([]byte(raw), &conf); err != nil { - return AuthConfig{}, xerrors.Errorf("parse docker auth config secret: %w", err) - } - if len(conf.AuthConfigs) != 1 { - return AuthConfig{}, xerrors.Errorf("number of image pull auth configs not equal to 1 (%d)", len(conf.AuthConfigs)) - } - for _, regConfig := range conf.AuthConfigs { - return AuthConfig(regConfig), nil - } - return AuthConfig{}, xerrors.New("no auth configs parsed.") } diff --git a/dockerutil/client_test.go b/dockerutil/client_test.go new file mode 100644 index 0000000..6e1baeb --- /dev/null +++ b/dockerutil/client_test.go @@ -0,0 +1,21 @@ +package dockerutil_test + +import ( + "testing" + + "github.com/coder/envbox/dockerutil" + "github.com/stretchr/testify/require" +) + +func TestAuthConfigFromString(t *testing.T) { + t.Parallel() + + creds := `{ "auths": { "docker.registry.test": { "auth": "Zm9vQGJhci5jb206YWJjMTIzCg==" } } }` + expectedUsername := "foo@bar.com" + expectedPassword := "abc123" + + cfg, err := dockerutil.AuthConfigFromString(creds, "docker.registry.test") + require.NoError(t, err) + require.Equal(t, expectedUsername, cfg.Username) + require.Equal(t, expectedPassword, cfg.Password) +} From 6bfdb320e076f8cb872d167dd052186e4fefe7ee Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Sun, 20 Oct 2024 22:13:44 +0000 Subject: [PATCH 2/7] echo -n --- dockerutil/client_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dockerutil/client_test.go b/dockerutil/client_test.go index 6e1baeb..2f09b0a 100644 --- a/dockerutil/client_test.go +++ b/dockerutil/client_test.go @@ -3,14 +3,15 @@ package dockerutil_test import ( "testing" - "github.com/coder/envbox/dockerutil" "github.com/stretchr/testify/require" + + "github.com/coder/envbox/dockerutil" ) func TestAuthConfigFromString(t *testing.T) { t.Parallel() - creds := `{ "auths": { "docker.registry.test": { "auth": "Zm9vQGJhci5jb206YWJjMTIzCg==" } } }` + creds := `{ "auths": { "docker.registry.test": { "auth": "Zm9vQGJhci5jb206YWJjMTIz" } } }` expectedUsername := "foo@bar.com" expectedPassword := "abc123" From 2d964d99e6232724b632e34cb4aa563432ec9851 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Sun, 20 Oct 2024 22:48:52 +0000 Subject: [PATCH 3/7] fix unit tests --- cli/docker_test.go | 4 ++-- dockerutil/client.go | 1 - dockerutil/client_test.go | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/docker_test.go b/cli/docker_test.go index 5f56f2e..5fdbb8a 100644 --- a/cli/docker_test.go +++ b/cli/docker_test.go @@ -384,13 +384,13 @@ func TestDocker(t *testing.T) { t.Parallel() ctx, cmd := clitest.New(t, "docker", - "--image=ubuntu", + "--image=us.gcr.io/ubuntu", "--username=root", "--agent-token=hi", fmt.Sprintf("--image-secret=%s", rawDockerAuth), ) - raw := []byte(`{"username":"_json_key","password":"{\"type\": \"service_account\", \"project_id\": \"some-test\", \"private_key_id\": \"blahblah\", \"private_key\": \"-----BEGIN PRIVATE KEY-----mykey-----END PRIVATE KEY-----\", \"client_email\": \"test@test.iam.gserviceaccount.com\", \"client_id\": \"123\", \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\", \"token_uri\": \"https://oauth2.googleapis.com/token\", \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\", \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/test.iam.gserviceaccount.com\" }","auth":"X2pzb25fa2V5OnsKCgkidHlwZSI6ICJzZXJ2aWNlX2FjY291bnQiLAoJInByb2plY3RfaWQiOiAic29tZS10ZXN0IiwKCSJwcml2YXRlX2tleV9pZCI6ICJibGFoYmxhaCIsCgkicHJpdmF0ZV9rZXkiOiAiLS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCm15a2V5LS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQoiLAoJImNsaWVudF9lbWFpbCI6ICJ0ZXN0QHRlc3QuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAoJImNsaWVudF9pZCI6ICIxMjMiLAoJImF1dGhfdXJpIjogImh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi9hdXRoIiwKCSJ0b2tlbl91cmkiOiAiaHR0cHM6Ly9vYXV0aDIuZ29vZ2xlYXBpcy5jb20vdG9rZW4iLAoJImF1dGhfcHJvdmlkZXJfeDUwOV9jZXJ0X3VybCI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9vYXV0aDIvdjEvY2VydHMiLAoJImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3JvYm90L3YxL21ldGFkYXRhL3g1MDkvdGVzdC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIKfQo=","email":"test@test.iam.gserviceaccount.com"}`) + raw := []byte(`{"username":"_json_key","password":"{\"type\": \"service_account\", \"project_id\": \"some-test\", \"private_key_id\": \"blahblah\", \"private_key\": \"-----BEGIN PRIVATE KEY-----mykey-----END PRIVATE KEY-----\", \"client_email\": \"test@test.iam.gserviceaccount.com\", \"client_id\": \"123\", \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\", \"token_uri\": \"https://oauth2.googleapis.com/token\", \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\", \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/test.iam.gserviceaccount.com\" }"}`) authB64 := base64.URLEncoding.EncodeToString(raw) client := clitest.DockerClient(t, ctx) diff --git a/dockerutil/client.go b/dockerutil/client.go index e9d1ef2..21d0442 100644 --- a/dockerutil/client.go +++ b/dockerutil/client.go @@ -89,5 +89,4 @@ func parseConfig(cfg dockercfg.Config, registry string) (AuthConfig, error) { } return AuthConfig{}, xerrors.Errorf("no auth config found for registry %s: %w", registry, os.ErrNotExist) - } diff --git a/dockerutil/client_test.go b/dockerutil/client_test.go index 2f09b0a..dec2fad 100644 --- a/dockerutil/client_test.go +++ b/dockerutil/client_test.go @@ -11,6 +11,7 @@ import ( func TestAuthConfigFromString(t *testing.T) { t.Parallel() + //nolint:gosec // this is a test creds := `{ "auths": { "docker.registry.test": { "auth": "Zm9vQGJhci5jb206YWJjMTIz" } } }` expectedUsername := "foo@bar.com" expectedPassword := "abc123" From eda7b5212cfb6ce0b98201a9666ace17dc9f9612 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Sun, 20 Oct 2024 22:50:35 +0000 Subject: [PATCH 4/7] fmt --- dockerutil/client.go | 1 - 1 file changed, 1 deletion(-) diff --git a/dockerutil/client.go b/dockerutil/client.go index 21d0442..d206219 100644 --- a/dockerutil/client.go +++ b/dockerutil/client.go @@ -68,7 +68,6 @@ func AuthConfigFromString(raw string, reg string) (AuthConfig, error) { } func parseConfig(cfg dockercfg.Config, registry string) (AuthConfig, error) { - hostname := dockercfg.ResolveRegistryHost(registry) username, secret, err := cfg.GetRegistryCredentials(hostname) From 2f785a98de7e74ef038e2e9409b4c48f578bb3f3 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Mon, 21 Oct 2024 00:00:07 +0000 Subject: [PATCH 5/7] add integration test --- integration/docker_test.go | 22 +++++ integration/integrationtest/docker.go | 112 +++++++++++++++++++++----- 2 files changed, 115 insertions(+), 19 deletions(-) diff --git a/integration/docker_test.go b/integration/docker_test.go index 54856bd..1419ff5 100644 --- a/integration/docker_test.go +++ b/integration/docker_test.go @@ -4,6 +4,7 @@ package integration_test import ( + "encoding/json" "fmt" "net" "os" @@ -17,6 +18,7 @@ import ( "github.com/stretchr/testify/require" "github.com/coder/envbox/cli" + "github.com/coder/envbox/dockerutil" "github.com/coder/envbox/integration/integrationtest" ) @@ -318,6 +320,9 @@ func TestDocker(t *testing.T) { regKeyPath := filepath.Join(certDir, "registry_key.pem") integrationtest.WriteCertificate(t, dockerCert, regCertPath, regKeyPath) + username := "coder" + password := "helloworld" + // Start up the docker registry and push an image // to it that we can reference. image := integrationtest.RunLocalDockerRegistry(t, pool, integrationtest.RegistryConfig{ @@ -325,12 +330,29 @@ func TestDocker(t *testing.T) { HostKeyPath: regKeyPath, Image: integrationtest.UbuntuImage, TLSPort: strconv.Itoa(registryAddr.Port), + PasswordDir: dir, + Username: username, + Password: password, }) + type authConfigs struct { + Auths map[string]dockerutil.AuthConfig `json:"auths"` + } + + auths := authConfigs{ + Auths: map[string]dockerutil.AuthConfig{ + image.Registry(): {Username: username, Password: password}, + }, + } + + authStr, err := json.Marshal(auths) + require.NoError(t, err) + envs := []string{ integrationtest.EnvVar(cli.EnvAgentToken, "faketoken"), integrationtest.EnvVar(cli.EnvAgentURL, fmt.Sprintf("https://%s:%d", "host.docker.internal", coderAddr.Port)), integrationtest.EnvVar(cli.EnvExtraCertsPath, "/tmp/certs"), + integrationtest.EnvVar(cli.EnvBoxPullImageSecretEnvVar, string(authStr)), } // Run the envbox container. diff --git a/integration/integrationtest/docker.go b/integration/integrationtest/docker.go index f24393c..76586ca 100644 --- a/integration/integrationtest/docker.go +++ b/integration/integrationtest/docker.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "crypto/tls" + "encoding/base64" "encoding/json" "fmt" "io" @@ -20,6 +21,7 @@ import ( "github.com/ory/dockertest/v3" "github.com/ory/dockertest/v3/docker" "github.com/stretchr/testify/require" + "golang.org/x/crypto/bcrypt" "golang.org/x/xerrors" "github.com/coder/envbox/buildlog" @@ -361,6 +363,10 @@ type RegistryConfig struct { HostKeyPath string TLSPort string Image string + Username string + Password string + // PasswordDir is the directory under which the htpasswd file is written. + PasswordDir string } type RegistryImage string @@ -373,28 +379,50 @@ func (r RegistryImage) String() string { return string(r) } -func RunLocalDockerRegistry(t testing.TB, pool *dockertest.Pool, conf RegistryConfig) RegistryImage { +func RunLocalDockerRegistry(t *testing.T, pool *dockertest.Pool, conf RegistryConfig) RegistryImage { t.Helper() const ( certPath = "/certs/cert.pem" keyPath = "/certs/key.pem" + authPath = "/auth/htpasswd" ) - resource, err := pool.RunWithOptions(&dockertest.RunOptions{ - Repository: registryImage, - Tag: registryTag, - Env: []string{ + var ( + envs = []string{ + EnvVar("REGISTRY_HTTP_ADDR", "0.0.0.0:443"), + } + binds []string + ) + + if conf.HostCertPath != "" && conf.HostKeyPath != "" { + envs = append(envs, EnvVar("REGISTRY_HTTP_TLS_CERTIFICATE", certPath), EnvVar("REGISTRY_HTTP_TLS_KEY", keyPath), - EnvVar("REGISTRY_HTTP_ADDR", "0.0.0.0:443"), - }, - ExposedPorts: []string{"443/tcp"}, - }, func(host *docker.HostConfig) { - host.Binds = []string{ + ) + binds = append(binds, mountBinding(conf.HostCertPath, certPath), mountBinding(conf.HostKeyPath, keyPath), - } + ) + } + + if conf.PasswordDir != "" { + authFile := GenerateRegistryAuth(t, conf.PasswordDir, conf.Username, conf.Password) + envs = append(envs, + EnvVar("REGISTRY_AUTH", "htpasswd"), + EnvVar("REGISTRY_AUTH_HTPASSWD_REALM", "Test Registry"), + EnvVar("REGISTRY_AUTH_HTPASSWD_PATH", authPath), + ) + binds = append(binds, mountBinding(authFile, authPath)) + } + + resource, err := pool.RunWithOptions(&dockertest.RunOptions{ + Repository: registryImage, + Tag: registryTag, + Env: envs, + ExposedPorts: []string{"443/tcp"}, + }, func(host *docker.HostConfig) { + host.Binds = binds host.ExtraHosts = []string{"host.docker.internal:host-gateway"} host.PortBindings = map[docker.Port][]docker.PortBinding{ "443/tcp": {{ @@ -415,7 +443,13 @@ func RunLocalDockerRegistry(t testing.TB, pool *dockertest.Pool, conf RegistryCo url := fmt.Sprintf("https://%s/v2/_catalog", host) waitForRegistry(t, pool, resource, url) - return pushLocalImage(t, pool, host, conf.Image) + return pushLocalImage(t, pool, pushOptions{ + Host: host, + RemoteImage: conf.Image, + Username: conf.Username, + Password: conf.Password, + ConfigDir: conf.PasswordDir, + }) } func waitForRegistry(t testing.TB, pool *dockertest.Pool, resource *dockertest.Resource, url string) { @@ -447,18 +481,26 @@ func waitForRegistry(t testing.TB, pool *dockertest.Pool, resource *dockertest.R continue } _ = res.Body.Close() - if res.StatusCode == http.StatusOK { + if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusUnauthorized { return } } require.NoError(t, ctx.Err()) } -func pushLocalImage(t testing.TB, pool *dockertest.Pool, host, remoteImage string) RegistryImage { +type pushOptions struct { + Host string + RemoteImage string + Username string + Password string + ConfigDir string +} + +func pushLocalImage(t *testing.T, pool *dockertest.Pool, opts pushOptions) RegistryImage { t.Helper() const registryHost = "127.0.0.1" - name := filepath.Base(remoteImage) + name := filepath.Base(opts.RemoteImage) repoTag := strings.Split(name, ":") tag := "latest" if len(repoTag) == 2 { @@ -469,25 +511,45 @@ func pushLocalImage(t testing.TB, pool *dockertest.Pool, host, remoteImage strin t: t, } err := pool.Client.PullImage(docker.PullImageOptions{ - Repository: strings.Split(remoteImage, ":")[0], + Repository: strings.Split(opts.RemoteImage, ":")[0], Tag: tag, OutputStream: tw, }, docker.AuthConfiguration{}) require.NoError(t, err) - _, port, err := net.SplitHostPort(host) + _, port, err := net.SplitHostPort(opts.Host) require.NoError(t, err) - err = pool.Client.TagImage(remoteImage, docker.TagImageOptions{ + err = pool.Client.TagImage(opts.RemoteImage, docker.TagImageOptions{ Repo: fmt.Sprintf("%s:%s/%s", registryHost, port, name), Tag: tag, }) require.NoError(t, err) + type config struct { + Auths map[string]dockerutil.AuthConfig `json:"auths"` + } + + auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", opts.Username, opts.Password))) + + cfg := config{ + Auths: map[string]dockerutil.AuthConfig{ + net.JoinHostPort(registryHost, port): { + Username: opts.Username, + Password: opts.Password, + Auth: auth, + }, + }, + } + b, err := json.Marshal(cfg) + require.NoError(t, err) + configPath := filepath.Join(opts.ConfigDir, "config.json") + WriteFile(t, configPath, string(b)) + // Idk what to tell you but the pool.Client.PushImage // function is bugged or I'm just dumb... image := fmt.Sprintf("%s:%s/%s:%s", registryHost, port, name, tag) - cmd := exec.Command("docker", "push", image) + cmd := exec.Command("docker", "--config", opts.ConfigDir, "push", image) cmd.Stderr = tw cmd.Stdout = tw err = cmd.Run() @@ -516,3 +578,15 @@ func BindMount(src, dst string, ro bool) docker.HostMount { Type: "bind", } } + +func GenerateRegistryAuth(t *testing.T, directory, username, password string) string { + t.Helper() + + p, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + require.NoError(t, err) + + authFile := filepath.Join(directory, "credentials") + WriteFile(t, authFile, fmt.Sprintf("%s:%s", username, string(p))) + + return authFile +} From 7d4a3b98107ae33062022e3b007938c32dccdc2e Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Mon, 21 Oct 2024 01:12:50 +0000 Subject: [PATCH 6/7] fix(integration): disable linter for docker push Add `//nolint:gosec` comment to disable linter for the `docker push` command in integration tests. This is necessary to bypass false positives related to the handling of the `exec.Command` which are considered secure in this test context. --- integration/integrationtest/docker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/integration/integrationtest/docker.go b/integration/integrationtest/docker.go index 76586ca..6b5e07c 100644 --- a/integration/integrationtest/docker.go +++ b/integration/integrationtest/docker.go @@ -549,6 +549,7 @@ func pushLocalImage(t *testing.T, pool *dockertest.Pool, opts pushOptions) Regis // Idk what to tell you but the pool.Client.PushImage // function is bugged or I'm just dumb... image := fmt.Sprintf("%s:%s/%s:%s", registryHost, port, name, tag) + //nolint:gosec cmd := exec.Command("docker", "--config", opts.ConfigDir, "push", image) cmd.Stderr = tw cmd.Stdout = tw From 3d29f75e4c1a5b06b92f402c593ffb99934dbf02 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Mon, 21 Oct 2024 15:48:47 +0000 Subject: [PATCH 7/7] unit tests --- cli/docker_test.go | 92 +++++++++++++++++++++++++++++++++++++++ dockerutil/client_test.go | 36 +++++++++++---- 2 files changed, 119 insertions(+), 9 deletions(-) diff --git a/cli/docker_test.go b/cli/docker_test.go index 5fdbb8a..3c077e8 100644 --- a/cli/docker_test.go +++ b/cli/docker_test.go @@ -63,6 +63,98 @@ func TestDocker(t *testing.T) { execer.AssertCommandsCalled(t) }) + t.Run("Images", func(t *testing.T) { + t.Parallel() + + type testcase struct { + name string + image string + success bool + } + + testcases := []testcase{ + { + name: "Repository", + image: "ubuntu", + success: true, + }, + { + name: "RepositoryPath", + image: "ubuntu/ubuntu", + success: true, + }, + + { + name: "RepositoryLatest", + image: "ubuntu:latest", + success: true, + }, + { + name: "RepositoryTag", + image: "ubuntu:24.04", + success: true, + }, + { + name: "RepositoryPathTag", + image: "ubuntu/ubuntu:18.04", + success: true, + }, + { + name: "RegistryRepository", + image: "gcr.io/ubuntu", + success: true, + }, + { + name: "RegistryRepositoryTag", + image: "gcr.io/ubuntu:24.04", + success: true, + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + ctx, cmd := clitest.New(t, "docker", + "--image="+tc.image, + "--username=root", + "--agent-token=hi", + ) + + called := make(chan struct{}) + execer := clitest.Execer(ctx) + client := clitest.DockerClient(t, ctx) + execer.AddCommands(&xunixfake.FakeCmd{ + FakeCmd: &testingexec.FakeCmd{ + Argv: []string{ + "sysbox-mgr", + }, + }, + WaitFn: func() error { close(called); select {} }, //nolint:revive + }) + + var created bool + client.ContainerCreateFn = func(_ context.Context, conf *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ *v1.Platform, _ string) (container.CreateResponse, error) { + created = true + require.Equal(t, tc.image, conf.Image) + return container.CreateResponse{}, nil + } + + err := cmd.ExecuteContext(ctx) + if !tc.success { + require.Error(t, err) + return + } + + <-called + require.NoError(t, err) + require.True(t, created, "container create fn not called") + execer.AssertCommandsCalled(t) + }) + } + }) + // Test that dockerd is configured correctly. t.Run("DockerdConfigured", func(t *testing.T) { t.Parallel() diff --git a/dockerutil/client_test.go b/dockerutil/client_test.go index dec2fad..b515632 100644 --- a/dockerutil/client_test.go +++ b/dockerutil/client_test.go @@ -11,13 +11,31 @@ import ( func TestAuthConfigFromString(t *testing.T) { t.Parallel() - //nolint:gosec // this is a test - creds := `{ "auths": { "docker.registry.test": { "auth": "Zm9vQGJhci5jb206YWJjMTIz" } } }` - expectedUsername := "foo@bar.com" - expectedPassword := "abc123" - - cfg, err := dockerutil.AuthConfigFromString(creds, "docker.registry.test") - require.NoError(t, err) - require.Equal(t, expectedUsername, cfg.Username) - require.Equal(t, expectedPassword, cfg.Password) + t.Run("Auth", func(t *testing.T) { + t.Parallel() + + //nolint:gosec // this is a test + creds := `{ "auths": { "docker.registry.test": { "auth": "Zm9vQGJhci5jb206YWJjMTIz" } } }` + expectedUsername := "foo@bar.com" + expectedPassword := "abc123" + + cfg, err := dockerutil.AuthConfigFromString(creds, "docker.registry.test") + require.NoError(t, err) + require.Equal(t, expectedUsername, cfg.Username) + require.Equal(t, expectedPassword, cfg.Password) + }) + + t.Run("UsernamePassword", func(t *testing.T) { + t.Parallel() + + //nolint:gosec // this is a test + creds := `{ "auths": { "docker.registry.test": { "username": "foobarbaz", "password": "123abc" } } }` + expectedUsername := "foobarbaz" + expectedPassword := "123abc" + + cfg, err := dockerutil.AuthConfigFromString(creds, "docker.registry.test") + require.NoError(t, err) + require.Equal(t, expectedUsername, cfg.Username) + require.Equal(t, expectedPassword, cfg.Password) + }) }