From 13c17d9c3c6f2c616885163bc6b1c85f15bc6068 Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Wed, 9 Aug 2023 12:35:58 +0300 Subject: [PATCH] feat: Add ability to push to multiple registries (#303) Co-authored-by: Robert Kaussow --- _docs/content/_index.md | 30 +++++++++++++++++++++++ _docs/data/data.yaml | 7 +++++- cmd/drone-docker-buildx/config.go | 9 ++++++- go.mod | 1 + plugin/docker.go | 10 ++++---- plugin/impl.go | 40 ++++++++++++++++++++++++++++--- 6 files changed, 88 insertions(+), 9 deletions(-) diff --git a/_docs/content/_index.md b/_docs/content/_index.md index ce1585e..d9ecb25 100644 --- a/_docs/content/_index.md +++ b/_docs/content/_index.md @@ -95,6 +95,36 @@ steps: tags: latest ``` +**Multiple registries:** + +```yaml +kind: pipeline +name: default + +steps: + - name: docker + image: thegeeklab/drone-docker-buildx:23 + privileged: true + environment: + DOCKER_REGISTRY_PASSWORD: + from_secret: docker_registry_password + GITHUB_REGISTRY_PASSWORD: + from_secret: github_registry_password + settings: + repo: + - octocat/example + - ghcr.io/octocat/example + tags: latest + registries: | + registries: + - username: "octocat" + password: "$DOCKER_REGISTRY_PASSWORD" + - registry: "ghcr.io" + username: "octocat" + password: "$GITHUB_REGISTRY_PASSWORD" +``` + + ## Build Build the binary with the following command: diff --git a/_docs/data/data.yaml b/_docs/data/data.yaml index 5981499..fea75e6 100644 --- a/_docs/data/data.yaml +++ b/_docs/data/data.yaml @@ -208,7 +208,7 @@ properties: description: | Repository name for the image. If the image is to be pushed to registries other than the default DockerHub, it is necessary to set `repo` as fully-qualified name. - type: string + type: list required: false - name: registry @@ -293,3 +293,8 @@ properties: This should be used with caution and avoided whenever possible. type: list required: false + + - name: registries + description: Credentials for multiple registries described in YAML format. Check out the Examples for more information. + type: string + required: false \ No newline at end of file diff --git a/cmd/drone-docker-buildx/config.go b/cmd/drone-docker-buildx/config.go index 8cc5415..afb3d8d 100644 --- a/cmd/drone-docker-buildx/config.go +++ b/cmd/drone-docker-buildx/config.go @@ -235,7 +235,7 @@ func settingsFlags(settings *plugin.Settings, category string) []cli.Flag { Destination: &settings.Build.Compress, Category: category, }, - &cli.StringFlag{ + &cli.StringSliceFlag{ Name: "repo", EnvVars: []string{"PLUGIN_REPO"}, Usage: "repository name for the image", @@ -328,5 +328,12 @@ func settingsFlags(settings *plugin.Settings, category string) []cli.Flag { Value: &drone.StringSliceFlag{}, Category: category, }, + &cli.StringFlag{ + Name: "docker.registries", + EnvVars: []string{"PLUGIN_REGISTRIES"}, + Usage: "credentials for registries", + Destination: &settings.Login.RegistriesYaml, + Category: category, + }, } } diff --git a/go.mod b/go.mod index 33a1914..bba9f4b 100644 --- a/go.mod +++ b/go.mod @@ -15,4 +15,5 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + gopkg.in/yaml.v3 v3.0.1 ) diff --git a/plugin/docker.go b/plugin/docker.go index 01db78d..3b2c0fa 100644 --- a/plugin/docker.go +++ b/plugin/docker.go @@ -11,7 +11,7 @@ import ( ) // helper function to create the docker login command. -func commandLogin(login Login) *execabs.Cmd { +func commandLogin(login RegistryData) *execabs.Cmd { if login.Email != "" { return commandLoginEmail(login) } @@ -28,7 +28,7 @@ func commandLogin(login Login) *execabs.Cmd { ) } -func commandLoginEmail(login Login) *execabs.Cmd { +func commandLoginEmail(login RegistryData) *execabs.Cmd { args := []string{ "login", "-u", login.Username, @@ -140,8 +140,10 @@ func commandBuild(build Build, dryrun bool) *execabs.Cmd { args = append(args, "--platform", strings.Join(build.Platforms.Value(), ",")) } - for _, arg := range build.Tags.Value() { - args = append(args, "-t", fmt.Sprintf("%s:%s", build.Repo, arg)) + for _, repo := range build.Repo.Value() { + for _, arg := range build.Tags.Value() { + args = append(args, "-t", fmt.Sprintf("%s:%s", repo, arg)) + } } for _, arg := range build.ExtraTags.Value() { diff --git a/plugin/impl.go b/plugin/impl.go index 6651bac..ca82cf7 100644 --- a/plugin/impl.go +++ b/plugin/impl.go @@ -9,6 +9,7 @@ import ( "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "golang.org/x/sys/execabs" + "gopkg.in/yaml.v3" ) // Daemon defines Docker daemon parameters. @@ -31,11 +32,20 @@ type Daemon struct { // Login defines Docker login parameters. type Login struct { + RegistryData + Config string // Docker Auth Config + RegistriesYaml string // Docker Auth with YAML config +} + +type RegistryData struct { 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 +} + +type RegistriesYaml struct { + Registries []RegistryData `yaml:"registries"` } // Build defines Docker build parameters. @@ -56,7 +66,7 @@ type Build struct { CacheFrom []string // Docker build cache-from CacheTo string // Docker build cache-to Compress bool // Docker build compress - Repo string // Docker build repository + Repo cli.StringSlice // Docker build repositories NoCache bool // Docker build no-cache AddHost cli.StringSlice // Docker build add-host Quiet bool // Docker build quiet @@ -167,7 +177,7 @@ func (p *Plugin) Execute() error { // login to the Docker registry if p.settings.Login.Password != "" { - cmd := commandLogin(p.settings.Login) + cmd := commandLogin(p.settings.Login.RegistryData) err := cmd.Run() if err != nil { @@ -175,6 +185,28 @@ func (p *Plugin) Execute() error { } } + if p.settings.Login.RegistriesYaml != "" { + var t RegistriesYaml + + err := yaml.Unmarshal([]byte(p.settings.Login.RegistriesYaml), &t) + if err != nil { + return fmt.Errorf("error unmarshal registries: %w", err) + } + + for _, registryData := range t.Registries { + if registryData.Registry == "" { + registryData.Registry = "https://index.docker.io/v1/" + } + + cmd := commandLogin(registryData) + + err := cmd.Run() + if err != nil { + return fmt.Errorf("error authenticating: %w", err) + } + } + } + if p.settings.Daemon.BuildkitConfig != "" { err := os.WriteFile(buildkitConfig, []byte(p.settings.Daemon.BuildkitConfig), strictFilePerm) if err != nil { @@ -185,6 +217,8 @@ func (p *Plugin) Execute() error { switch { case p.settings.Login.Password != "": logrus.Info("Detected registry credentials") + case p.settings.Login.RegistriesYaml != "": + logrus.Info("Detected multiple registry credentials") case p.settings.Login.Config != "": logrus.Info("Detected registry credentials file") default: