func trace(cmd *exec.Cmd) {
	fmt.Fprintf(os.Stdout, "+ %s\n", strings.Join(cmd.Args, " "))
}
trace writes each command to stdout with the command wrapped in an xml
tag so that it can be extracted and displayed in the logs.
package docker
import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
)
type (
	Login struct {
		Registry string  
		Username string  
		Password string  
		Email    string  
	}
	Host struct {
		Host      string  
		UseTLS    bool    
		TLSVerify bool    
	}
	Deploy struct {
		Name    string  
		Compose string  
		Prune   bool    
	}
	Certs struct {
		TLSKey    string  
		TLSCert   string  
		TLSCACert string  
	}
	Plugin struct {
		Login  Login   
		Deploy Deploy  
		Certs  Certs   
		Host   Host    
	}
)
const dockerExe = "/usr/local/bin/docker"
const certdir = "/tmp/certs"
func (p Plugin) Exec() error {
	err := setupCerts(p.Certs, p.Host)
	if err != nil {
		return fmt.Errorf("cannot setup certificates: %s", err)
	}
	if p.Host.Host == "" || !strings.HasPrefix(p.Host.Host, "tcp://") {
		return fmt.Errorf("docker host must be present and use the format tcp://..., provided: %s", p.Host.Host)
	}
	if p.Deploy.Name == "" {
		return fmt.Errorf("docker stack name must be present")
	}
	envs := []string{
		fmt.Sprintf("DOCKER_CERT_PATH=%s", certdir),
		fmt.Sprintf("DOCKER_HOST=%s", p.Host.Host),
	}
	if p.Host.TLSVerify {
		envs = append(envs, "DOCKER_TLS_VERIFY=1")
	} else if p.Host.UseTLS {
		envs = append(envs, "DOCKER_TLS=1")
	}
	var registryAuth bool
	if p.Login.Password != "" {
		registryAuth = true
		cmd := commandLogin(p.Login)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Env = envs
		err := cmd.Run()
		if err != nil {
			return fmt.Errorf("error authenticating: %s", err)
		}
	} else {
		registryAuth = false
		fmt.Println("Registry credentials not provided. Guest mode enabled.")
	}
	var cmds []*exec.Cmd
	cmds = append(cmds, commandVersion())                       
	cmds = append(cmds, commandInfo())                          
	cmds = append(cmds, commandDeploy(p.Deploy, registryAuth))  
	for _, cmd := range cmds {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Env = envs
		trace(cmd)
		err := cmd.Run()
		if err != nil {
			return err
		}
	}
	return nil
}
func setupCerts(certs Certs, host Host) error {
	if host.UseTLS || host.TLSVerify {
		err := os.MkdirAll(certdir, 0755)
		if err != nil {
			return fmt.Errorf("cannot create cert directory: %s", err)
		}
		if (certs.TLSKey != "") == (certs.TLSCert != "") {
			if certs.TLSKey != "" {
				err := ioutil.WriteFile(path.Join(certdir, "key.pem"), []byte(certs.TLSKey), 0600)
				if err != nil {
					return fmt.Errorf("cannot create key.pem: %s", err)
				}
				err = ioutil.WriteFile(path.Join(certdir, "cert.pem"), []byte(certs.TLSCert), 0644)
				if err != nil {
					return fmt.Errorf("cannot create cert.pem: %s", err)
				}
			}
		} else if certs.TLSKey != "" {
			fmt.Printf("the client certificate must be present")
		} else {
			fmt.Printf("the client key must be present")
		}
		if host.TLSVerify {
			if certs.TLSCACert != "" {
				err := ioutil.WriteFile(path.Join(certdir, "ca.pem"), []byte(certs.TLSCACert), 0644)
				if err != nil {
					return fmt.Errorf("cannot create ca.pem: %s", err)
				}
			} else {
				return fmt.Errorf("cannot use tlsverify without a given CA")
			}
		}
	}
	return nil
}
func commandLogin(login Login) *exec.Cmd {
	if login.Email != "" {
		return commandLoginEmail(login)
	}
	return exec.Command(
		dockerExe, "login",
		"-u", login.Username,
		"-p", login.Password,
		login.Registry,
	)
}
func commandLoginEmail(login Login) *exec.Cmd {
	return exec.Command(
		dockerExe, "login",
		"-u", login.Username,
		"-p", login.Password,
		"-e", login.Email,
		login.Registry,
	)
}
func commandVersion() *exec.Cmd {
	return exec.Command(dockerExe, "version")
}
func commandInfo() *exec.Cmd {
	return exec.Command(dockerExe, "info")
}
func commandDeploy(deploy Deploy, auth bool) *exec.Cmd {
	args := []string{
		"stack",
		"deploy",
		"-c", deploy.Compose,
		deploy.Name,
	}
	if deploy.Prune {
		args = append(args, "--prune")
	}
	if auth {
		args = append(args, "--with-registry-auth")
	}
	return exec.Command(dockerExe, args...)
}