Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
18 changes: 18 additions & 0 deletions cmd/drone-docker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,21 @@ func main() {
Usage: "docker password",
EnvVar: "PLUGIN_PASSWORD,DOCKER_PASSWORD",
},
cli.StringFlag{
Name: "docker.baseimageusername",
Usage: "Docker username for base image registry",
EnvVar: "PLUGIN_DOCKER_USERNAME,PLUGIN_BASE_IMAGE_USERNAME",
},
cli.StringFlag{
Name: "docker.baseimagepassword",
Usage: "Docker password for base image registry",
EnvVar: "PLUGIN_DOCKER_PASSWORD,PLUGIN_BASE_IMAGE_PASSWORD",
},
cli.StringFlag{
Name: "docker.baseimageregistry",
Usage: "Docker registry for base image registry",
EnvVar: "PLUGIN_DOCKER_REGISTRY,PLUGIN_BASE_IMAGE_REGISTRY",
},
cli.StringFlag{
Name: "docker.email",
Usage: "docker email",
Expand Down Expand Up @@ -367,6 +382,9 @@ func run(c *cli.Context) error {
Experimental: c.Bool("daemon.experimental"),
RegistryType: registryType,
},
BaseImageRegistry: c.String("docker.baseimageregistry"),
BaseImageUsername: c.String("docker.baseimageusername"),
BaseImagePassword: c.String("docker.baseimagepassword"),
}

if c.Bool("tags.auto") {
Expand Down
78 changes: 69 additions & 9 deletions docker.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package docker

import (
"errors"
"fmt"
"os"
"os/exec"
Expand All @@ -10,7 +9,9 @@ import (
"strings"
"time"

"github.com/drone-plugins/drone-docker/internal/docker"
"github.com/drone-plugins/drone-plugin-lib/drone"
"github.com/pkg/errors"
)

type (
Expand Down Expand Up @@ -75,13 +76,16 @@ type (

// Plugin defines the Docker plugin parameters.
Plugin struct {
Login Login // Docker login configuration
Build Build // Docker build configuration
Daemon Daemon // Docker daemon configuration
Dryrun bool // Docker push is skipped
Cleanup bool // Docker purge is enabled
CardPath string // Card path to write file to
ArtifactFile string // Artifact path to write file to
Login Login // Docker login configuration
Build Build // Docker build configuration
Daemon Daemon // Docker daemon configuration
Dryrun bool // Docker push is skipped
Cleanup bool // Docker purge is enabled
CardPath string // Card path to write file to
ArtifactFile string // Artifact path to write file to
BaseImageRegistry string // Docker registry to pull base image
BaseImageUsername string // Docker registry username to pull base image
BaseImagePassword string // Docker registry password to pull base image
}

Card []struct {
Expand Down Expand Up @@ -154,10 +158,37 @@ func (p Plugin) Exec() error {
os.MkdirAll(dockerHome, 0600)

path := filepath.Join(dockerHome, "config.json")
err := os.WriteFile(path, []byte(p.Login.Config), 0600)
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return fmt.Errorf("Error writing config.json: %s", err)
}
_, err = file.Write([]byte(p.Login.Config))
fmt.Println("Writing p.Login.Config: %s", p.Login.Config)

if err != nil {
return fmt.Errorf("Error writing config.json: %s", err)
}
defer file.Close()
}
// add base image docker credentials to the existing config file, else create new
if p.BaseImagePassword != "" {
json, err := setDockerAuth(p.Login.Username, p.Login.Password, p.Login.Registry,
p.BaseImageUsername, p.BaseImagePassword, p.BaseImageRegistry)
if err != nil {
return errors.Wrap(err, "Failed to set authentication in docker config")
}
if json != nil {
path := filepath.Join(dockerHome, "config.json")
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return fmt.Errorf("Error opening config.json: %s", err)
}
defer file.Close()
_, err = file.Write(json)
if err != nil {
return fmt.Errorf("Error writing config.json: %s", err)
}
}
}

// login to the Docker registry
Expand Down Expand Up @@ -270,6 +301,35 @@ func (p Plugin) Exec() error {
return nil
}

// helper function to set the credentials
func setDockerAuth(username, password, registry, baseImageUsername,
baseImagePassword, baseImageRegistry string) ([]byte, error) {
var credentials []docker.RegistryCredentials
// add only docker registry to the config
if password != "" && strings.Contains(registry, "docker") {
dockerConfig := docker.NewConfig()
pushToRegistryCreds := docker.RegistryCredentials{
Registry: registry,
Username: username,
Password: password,
}
// push registry auth
credentials := append(credentials, pushToRegistryCreds)
}

if baseImageRegistry != "" {
pullFromRegistryCreds := docker.RegistryCredentials{
Registry: baseImageRegistry,
Username: baseImageUsername,
Password: baseImagePassword,
}
// base image registry auth
credentials = append(credentials, pullFromRegistryCreds)
}
// Creates docker config for both the registries used for authentication
return dockerConfig.CreateDockerConfigJson(credentials)
}

// helper function to create the docker login command.
func commandLogin(login Login) *exec.Cmd {
if login.Email != "" {
Expand Down
91 changes: 91 additions & 0 deletions internal/docker/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package docker

import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"

"github.com/pkg/errors"
)

const (
v2HubRegistryURL string = "https://registry.hub.docker.com/v2/"
v1RegistryURL string = "https://index.docker.io/v1/" // Default registry
v2RegistryURL string = "https://index.docker.io/v2/" // v2 registry is not supported
)

type (
Auth struct {
Auth string `json:"auth"`
}

Config struct {
Auths map[string]Auth `json:"auths"`
CredHelpers map[string]string `json:"credHelpers,omitempty"`
}
)

type RegistryCredentials struct {
Registry string
Username string
Password string
}

func NewConfig() *Config {
return &Config{
Auths: make(map[string]Auth),
CredHelpers: make(map[string]string),
}
}

func (c *Config) SetAuth(registry, username, password string) {
authBytes := []byte(username + ":" + password)
encodedString := base64.StdEncoding.EncodeToString(authBytes)
log.Printf("auth : %s", encodedString)
c.Auths[registry] = Auth{Auth: encodedString}
}

func (c *Config) SetCredHelper(registry, helper string) {
c.CredHelpers[registry] = helper
}

func (c *Config) CreateDockerConfigJson(credentials []RegistryCredentials) ([]byte, error) {
for _, cred := range credentials {
if cred.Registry != "" && strings.Contains(cred.Registry, "docker") {

if cred.Username == "" {
return nil, fmt.Errorf("Username must be specified for registry: %s", cred.Registry)
}
if cred.Password == "" {
return nil, fmt.Errorf("Password must be specified for registry: %s", cred.Registry)
}
c.SetAuth(cred.Registry, cred.Username, cred.Password)
}
}

jsonBytes, err := json.Marshal(c)
if err != nil {
return nil, errors.Wrap(err, "failed to serialize docker config json")
}

return jsonBytes, nil
}

func WriteDockerConfig(data []byte, path string) (string error) {
err := os.MkdirAll(path, 0600)
if err != nil {
if !os.IsExist(err) {
return errors.Wrap(err, fmt.Sprintf("failed to create %s directory", path))
}
}

filePath := path + "/config.json"
log.Printf("Config data is %s", data)
err = ioutil.WriteFile(filePath, data, 0644)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to create docker config file at %s", path))
}
return nil
}
64 changes: 64 additions & 0 deletions internal/docker/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package docker

import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

const (
RegistryV1 string = "https://index.docker.io/v1/"
RegistryV2 string = "https://index.docker.io/v2/"
RegistryECRPublic string = "public.ecr.aws"
)

func TestConfig(t *testing.T) {
c := NewConfig()
assert.NotNil(t, c.Auths)
assert.NotNil(t, c.CredHelpers)

c.SetAuth(RegistryV1, "test", "password")
expectedAuth := Auth{Auth: "dGVzdDpwYXNzd29yZA=="}
assert.Equal(t, expectedAuth, c.Auths[RegistryV1])

c.SetCredHelper(RegistryECRPublic, "ecr-login")
assert.Equal(t, "ecr-login", c.CredHelpers[RegistryECRPublic])

tempDir, err := ioutil.TempDir("", "docker-config-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)

credentials := []RegistryCredentials{
{
Registry: "https://index.docker.io/v1/",
Username: "user1",
Password: "pass1",
},
{
Registry: "gcr.io",
Username: "user2",
Password: "pass2",
},
}

jsonBytes, err := c.CreateDockerConfigJson(credentials)
assert.NoError(t, err)

configPath := filepath.Join(tempDir, "config.json")
err = ioutil.WriteFile(configPath, jsonBytes, 0644)
assert.NoError(t, err)

data, err := ioutil.ReadFile(configPath)
assert.NoError(t, err)

var configFromFile Config
err = json.Unmarshal(data, &configFromFile)
assert.NoError(t, err)

assert.Equal(t, c.Auths, configFromFile.Auths)
assert.Equal(t, c.CredHelpers, configFromFile.CredHelpers)
}