diff --git a/cmd/drone-docker/main.go b/cmd/drone-docker/main.go index 77343f8..2cd9275 100644 --- a/cmd/drone-docker/main.go +++ b/cmd/drone-docker/main.go @@ -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", @@ -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") { diff --git a/cmd/drone-gar/main.go b/cmd/drone-gar/main.go index 235b075..52f8a83 100644 --- a/cmd/drone-gar/main.go +++ b/cmd/drone-gar/main.go @@ -27,6 +27,9 @@ type Config struct { WorkloadIdentity bool Username string AccessToken string + 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 } type staticTokenSource struct { @@ -100,18 +103,22 @@ func main() { os.Setenv("DOCKER_USERNAME", config.Username) os.Setenv("DOCKER_PASSWORD", config.Password) } + //data, err := ioutil.ReadFile("/.docker/config.json") + fmt.Println(" Aishwarya config.json is 1.." ) os.Setenv("PLUGIN_REPO", config.Repo) os.Setenv("PLUGIN_REGISTRY", config.Registry) // invoke the base docker plugin binary cmd := exec.Command(docker.GetDroneDockerExecCmd()) + fmt.Println(" Aishwarya config.json is 2.." ) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { logrus.Fatal(err) } + fmt.Println(" Aishwarya config.json is 4.." ) } func getOauthToken(data []byte) (s string) { diff --git a/docker.go b/docker.go index 475c2d8..b3c67cc 100644 --- a/docker.go +++ b/docker.go @@ -1,8 +1,8 @@ package docker import ( - "errors" "fmt" + "log" "os" "os/exec" "path/filepath" @@ -10,7 +10,10 @@ import ( "strings" "time" + "github.com/drone-plugins/drone-docker/internal/docker" "github.com/drone-plugins/drone-plugin-lib/drone" + "github.com/pkg/errors" + ) type ( @@ -75,13 +78,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 { @@ -154,11 +160,42 @@ 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() } + log.Printf("p.Login.Config .... %s", p.Login.Config) + // add docker credentials to the existing config file, else create new + if p.Login.Password != "" && p.BaseImagePassword != "" { + json, err := setDockerAuth(p.Login.Username, p.Login.Password, p.Login.Registry, + p.BaseImageUsername, p.BaseImagePassword, p.BaseImageRegistry) + fmt.Println("json after set Auth: %s", json) + 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) + } + } + } + fmt.Println("json after set Auth: %s", json) + // login to the Docker registry if p.Login.Password != "" { @@ -270,6 +307,32 @@ func (p Plugin) Exec() error { return nil } +// helper function to set the credentials +func setDockerAuth(username, password, registry, baseImageUsername, + baseImagePassword, baseImageRegistry string) ([]byte, error) { + dockerConfig := docker.NewConfig() + pushToRegistryCreds := docker.RegistryCredentials{ + Registry: registry, + Username: username, + Password: password, + } + // push registry auth + //credentials := []docker.RegistryCredentials{pushToRegistryCreds} + credentials := []docker.RegistryCredentials{} + + 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 != "" { @@ -504,6 +567,7 @@ func commandPush(build Build, tag string) *exec.Cmd { // helper function to create the docker daemon command. func commandDaemon(daemon Daemon) *exec.Cmd { + fmt.Println(" Aishwarya config.json is 5.." ) args := []string{ "--data-root", daemon.StoragePath, "--host=unix:///var/run/docker.sock", @@ -585,6 +649,8 @@ func trace(cmd *exec.Cmd) { } func GetDroneDockerExecCmd() string { + fmt.Println(" Aishwarya config.json is 3.." ) + if runtime.GOOS == "windows" { return "C:/bin/drone-docker.exe" } diff --git a/internal/docker/config.go b/internal/docker/config.go new file mode 100644 index 0000000..1d5a0bd --- /dev/null +++ b/internal/docker/config.go @@ -0,0 +1,93 @@ +package docker + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "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 != "" { + + 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) + log.Printf("jsonBytes config : %s", jsonBytes) + 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 +}