diff --git a/.gitignore b/.gitignore index afd7d10..3389027 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ plugin-docker-buildx coverage.out CHANGELOG.md +debug.test* diff --git a/cmd/docker-buildx/main.go b/cmd/docker-buildx/main.go index 6fceb2f..cedfdc2 100644 --- a/cmd/docker-buildx/main.go +++ b/cmd/docker-buildx/main.go @@ -14,7 +14,9 @@ import ( var version = "unknown" func main() { - settings := &plugin.Settings{} + settings := &plugin.Settings{ + CustomCertStore: "/etc/docker/certs.d/", + } if _, err := os.Stat("/run/drone/env"); err == nil { godotenv.Overload("/run/drone/env") diff --git a/docs.md b/docs.md index 9a25af8..a24b2f4 100644 --- a/docs.md +++ b/docs.md @@ -105,7 +105,7 @@ docker-build: | `experimental` | `false` | enables docker daemon experimental mode | | `debug` | `false` | enables verbose debug mode for the docker daemon | | `daemon_off` | `false` | disables the startup of the docker daemon | -| `buildkit_debug` | _none_ | enables debug output of buildkit +| `buildkit_debug` | `false` | enables debug output of buildkit | | `buildkit_config` | _none_ | sets content of the docker [buildkit TOML config](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md) | | `buildkit_driveropt` | _none_ | adds one or multiple `--driver-opt` buildx arguments for the default buildkit builder instance | | `tags_file` | _none_ | overrides the `tags` option with values in a file named `.tags`; multiple tags can be specified separated by a newline | @@ -147,6 +147,8 @@ settings: username: a6543 password: from_secret: docker_token + mirrors: + - "my-docker-mirror-host.local" - registry: https://codeberg.org username: "6543" password: diff --git a/plugin/docker.go b/plugin/docker.go index 750810a..e5d2ff4 100644 --- a/plugin/docker.go +++ b/plugin/docker.go @@ -179,9 +179,6 @@ func commandDaemon(daemon Daemon) *exec.Cmd { if daemon.IPv6 { args = append(args, "--ipv6") } - if len(daemon.Mirror) != 0 { - args = append(args, "--registry-mirror", daemon.Mirror) - } if len(daemon.Bip) != 0 { args = append(args, "--bip", daemon.Bip) } diff --git a/plugin/impl.go b/plugin/impl.go index 83301d7..f920ef4 100644 --- a/plugin/impl.go +++ b/plugin/impl.go @@ -40,12 +40,12 @@ type Daemon struct { // Login defines Docker login parameters. type Login struct { // Generic - Registry string // Docker registry address - Username string // Docker registry username - Password string // Docker registry password - Email string // Docker registry email - Config string // Docker Auth Config - + Registry string // Docker registry address + Username string // Docker registry username + Password string // Docker registry password + Email string // Docker registry email + Config string // Docker Auth Config + Mirrors []string // Docker registry mirrors // ECR Aws_access_key_id string `json:"aws_access_key_id"` // AWS access key id Aws_secret_access_key string `json:"aws_secret_access_key"` // AWS secret access key @@ -93,13 +93,14 @@ type Settings struct { AwsSecretAccessKey string `json:"aws_secret_access_key"` // AWS secret access key // Generic - Daemon Daemon - Logins []Login - LoginsRaw string - DefaultLogin Login - Build Build - Dryrun bool - Cleanup bool + Daemon Daemon + Logins []Login + LoginsRaw string + DefaultLogin Login + Build Build + Dryrun bool + Cleanup bool + CustomCertStore string // e.g. for "/etc/docker/certs.d//ca.crt" } func (l Login) anonymous() bool { @@ -128,6 +129,10 @@ func (p *Plugin) InitSettings() error { p.EcrInit() } + if p.settings.DefaultLogin.Registry != "" && p.settings.Daemon.Mirror != "" { + p.settings.DefaultLogin.Mirrors = []string{p.settings.Daemon.Mirror} + } + if len(p.settings.Logins) == 0 { p.settings.Logins = []Login{p.settings.DefaultLogin} } else if !p.settings.DefaultLogin.anonymous() { @@ -224,13 +229,13 @@ func (p *Plugin) sanitizedUserTags() []string { } type BuildkitConfigTOML struct { - Debug bool `toml:"debug"` // needs to be public for toml lib to use - Registry map[string]RegistryInfo `toml:"registry"` + Debug bool `toml:"debug,omitempty"` // needs to be public for toml lib to use + Registry map[string]*RegistryInfo `toml:"registry,omitempty"` } type RegistryInfo struct { - Mirrors []string `toml:"mirrors"` - CA []string `toml:"ca"` + Mirrors []string `toml:"mirrors,omitempty"` + CA []string `toml:"ca,omitempty"` } func (p *Plugin) generateBuildkitConfig() error { @@ -238,19 +243,13 @@ func (p *Plugin) generateBuildkitConfig() error { if p.settings.Daemon.BuildkitConfig == "" { cfg := BuildkitConfigTOML{} - cfg.Registry = map[string]RegistryInfo{} + cfg.Registry = make(map[string]*RegistryInfo) if p.settings.Daemon.BuildkitDebug { cfg.Debug = p.settings.Daemon.BuildkitDebug logrus.Println("buildkit debug enabled") } - if p.settings.Daemon.Mirror != "" { - cfg.Registry["docker.io"] = RegistryInfo{ - Mirrors: []string{p.settings.Daemon.Mirror}, - } - } - // use a custom CA certificate for each registry if p.settings.Daemon.Registry != "" { for _, login := range p.settings.Logins { @@ -263,16 +262,30 @@ func (p *Plugin) generateBuildkitConfig() error { registry = u.Host } - caPath := fmt.Sprintf("/etc/docker/certs.d/%s/ca.crt", registry) + // docker hub fix + if registry == "index.docker.io" { + registry = "docker.io" + } + + caPath := fmt.Sprintf("%s/%s/ca.crt", p.settings.CustomCertStore, registry) ca, err := os.Open(caPath) if err != nil && !os.IsNotExist(err) { logrus.Warnf("error reading %s: %v", caPath, err) } else if err == nil { ca.Close() + logrus.Infof("found ca file for '%s' registry", registry) // add registry and ca path to buildkit.toml - cfg.Registry[registry] = RegistryInfo{ - CA: []string{caPath}, + if cfg.Registry[registry] == nil { + cfg.Registry[registry] = new(RegistryInfo) } + cfg.Registry[registry].CA = []string{caPath} + } + + if len(login.Mirrors) != 0 { + if cfg.Registry[registry] == nil { + cfg.Registry[registry] = new(RegistryInfo) + } + cfg.Registry[registry].Mirrors = login.Mirrors } } } diff --git a/plugin/impl_test.go b/plugin/impl_test.go index c65f962..4232af0 100644 --- a/plugin/impl_test.go +++ b/plugin/impl_test.go @@ -1,6 +1,8 @@ package plugin import ( + "fmt" + "os" "testing" "codeberg.org/6543/go-yaml2json" @@ -8,7 +10,7 @@ import ( "github.com/urfave/cli/v2" ) -var defaultSettings = Settings{ +var defaultTestSettings = Settings{ Daemon: Daemon{ StoragePath: "/var/lib/docker", }, @@ -22,15 +24,16 @@ var defaultSettings = Settings{ DefaultLogin: Login{ Registry: "https://index.docker.io/v1/", }, - LoginsRaw: "[]", - Cleanup: true, + LoginsRaw: "[]", + Cleanup: true, + CustomCertStore: "/etc/docker/certs.d/", } func TestDefaultLogin(t *testing.T) { - s := defaultSettings + s := defaultTestSettings assert.NoError(t, newSettingsOnly(&s).Validate()) if assert.Len(t, s.Logins, 1) { - assert.EqualValues(t, defaultSettings.DefaultLogin.Registry, s.Logins[0].Registry) + assert.EqualValues(t, defaultTestSettings.DefaultLogin.Registry, s.Logins[0].Registry) } // only use login to auth to registrys @@ -45,11 +48,11 @@ func TestDefaultLogin(t *testing.T) { s.LoginsRaw = string(loginsRaw) assert.NoError(t, newSettingsOnly(&s).Validate()) if assert.Len(t, s.Logins, 2) { - assert.EqualValues(t, defaultSettings.DefaultLogin.Registry, s.Logins[0].Registry) + assert.EqualValues(t, defaultTestSettings.DefaultLogin.Registry, s.Logins[0].Registry) } // mixed login settings ('logins' and 'username', 'password' are used) - s = defaultSettings + s = defaultTestSettings loginsRaw, err = yaml2json.Convert([]byte(` - registry: https://codeberg.org username: cb_username @@ -60,11 +63,11 @@ func TestDefaultLogin(t *testing.T) { s.DefaultLogin.Password = "docker_password" assert.NoError(t, newSettingsOnly(&s).Validate()) if assert.Len(t, s.Logins, 2) { - assert.EqualValues(t, defaultSettings.DefaultLogin.Registry, s.Logins[0].Registry) + assert.EqualValues(t, defaultTestSettings.DefaultLogin.Registry, s.Logins[0].Registry) } // ignore default registry - s = defaultSettings + s = defaultTestSettings loginsRaw, err = yaml2json.Convert([]byte(` - registry: https://codeberg.org username: cb_username @@ -78,17 +81,31 @@ func TestDefaultLogin(t *testing.T) { } func TestWriteBuildkitConfig(t *testing.T) { - settings := defaultSettings + settings := defaultTestSettings assert.NoError(t, newSettingsOnly(&settings).Validate()) assert.EqualValues(t, "", settings.Daemon.BuildkitConfig) - settings = defaultSettings + settings = defaultTestSettings settings.Daemon.BuildkitDebug = true assert.NoError(t, newSettingsOnly(&settings).Validate()) - assert.EqualValues(t, "debug = true\n\n[registry]\n", settings.Daemon.BuildkitConfig) + assert.EqualValues(t, "debug = true\n", settings.Daemon.BuildkitConfig) - settings = defaultSettings + settings = defaultTestSettings settings.Daemon.Mirror = "mirror.example.com" assert.NoError(t, newSettingsOnly(&settings).Validate()) - assert.EqualValues(t, "debug = false\n\n[registry]\n[registry.'docker.io']\nmirrors = ['mirror.example.com']\nca = []\n", settings.Daemon.BuildkitConfig) + assert.EqualValues(t, "[registry]\n[registry.'docker.io']\nmirrors = ['mirror.example.com']\n", settings.Daemon.BuildkitConfig) + + settings = defaultTestSettings + settings.DefaultLogin.Registry = "codeberg.org" + tmpDir, err := os.MkdirTemp("", "go-test-*") + assert.NoError(t, err) + settings.CustomCertStore = tmpDir + defer os.RemoveAll(tmpDir) + assert.NoError(t, os.Mkdir(tmpDir+"/codeberg.org", os.ModePerm)) + caFile, err := os.Create(tmpDir + "/codeberg.org/" + "ca.crt") + assert.NoError(t, err) + assert.NoError(t, caFile.Close()) + + assert.NoError(t, newSettingsOnly(&settings).Validate()) + assert.EqualValues(t, fmt.Sprintf("[registry]\n[registry.'codeberg.org']\nca = ['%s/codeberg.org/ca.crt']\n", tmpDir), settings.Daemon.BuildkitConfig) }