github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/docker/driver_docker.go (about)

     1  package docker
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"os"
     9  	"os/exec"
    10  	"regexp"
    11  	"runtime"
    12  	"strings"
    13  	"sync"
    14  
    15  	"github.com/hashicorp/go-version"
    16  	"github.com/hashicorp/packer/packer"
    17  	"github.com/hashicorp/packer/template/interpolate"
    18  )
    19  
    20  type DockerDriver struct {
    21  	Ui  packer.Ui
    22  	Ctx *interpolate.Context
    23  
    24  	l sync.Mutex
    25  }
    26  
    27  func (d *DockerDriver) DeleteImage(id string) error {
    28  	var stderr bytes.Buffer
    29  	cmd := exec.Command("docker", "rmi", id)
    30  	cmd.Stderr = &stderr
    31  
    32  	log.Printf("Deleting image: %s", id)
    33  	if err := cmd.Start(); err != nil {
    34  		return err
    35  	}
    36  
    37  	if err := cmd.Wait(); err != nil {
    38  		err = fmt.Errorf("Error deleting image: %s\nStderr: %s",
    39  			err, stderr.String())
    40  		return err
    41  	}
    42  
    43  	return nil
    44  }
    45  
    46  func (d *DockerDriver) Commit(id string, author string, changes []string, message string) (string, error) {
    47  	var stdout bytes.Buffer
    48  	var stderr bytes.Buffer
    49  
    50  	args := []string{"commit"}
    51  	if author != "" {
    52  		args = append(args, "--author", author)
    53  	}
    54  	for _, change := range changes {
    55  		args = append(args, "--change", change)
    56  	}
    57  	if message != "" {
    58  		args = append(args, "--message", message)
    59  	}
    60  	args = append(args, id)
    61  
    62  	log.Printf("Committing container with args: %v", args)
    63  	cmd := exec.Command("docker", args...)
    64  	cmd.Stdout = &stdout
    65  	cmd.Stderr = &stderr
    66  
    67  	if err := cmd.Start(); err != nil {
    68  		return "", err
    69  	}
    70  
    71  	if err := cmd.Wait(); err != nil {
    72  		err = fmt.Errorf("Error committing container: %s\nStderr: %s",
    73  			err, stderr.String())
    74  		return "", err
    75  	}
    76  
    77  	return strings.TrimSpace(stdout.String()), nil
    78  }
    79  
    80  func (d *DockerDriver) Export(id string, dst io.Writer) error {
    81  	var stderr bytes.Buffer
    82  	cmd := exec.Command("docker", "export", id)
    83  	cmd.Stdout = dst
    84  	cmd.Stderr = &stderr
    85  
    86  	log.Printf("Exporting container: %s", id)
    87  	if err := cmd.Start(); err != nil {
    88  		return err
    89  	}
    90  
    91  	if err := cmd.Wait(); err != nil {
    92  		err = fmt.Errorf("Error exporting: %s\nStderr: %s",
    93  			err, stderr.String())
    94  		return err
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  func (d *DockerDriver) Import(path string, repo string) (string, error) {
   101  	var stdout, stderr bytes.Buffer
   102  	cmd := exec.Command("docker", "import", "-", repo)
   103  	cmd.Stdout = &stdout
   104  	cmd.Stderr = &stderr
   105  	stdin, err := cmd.StdinPipe()
   106  	if err != nil {
   107  		return "", err
   108  	}
   109  
   110  	// There should be only one artifact of the Docker builder
   111  	file, err := os.Open(path)
   112  	if err != nil {
   113  		return "", err
   114  	}
   115  	defer file.Close()
   116  
   117  	if err := cmd.Start(); err != nil {
   118  		return "", err
   119  	}
   120  
   121  	go func() {
   122  		defer stdin.Close()
   123  		io.Copy(stdin, file)
   124  	}()
   125  
   126  	if err := cmd.Wait(); err != nil {
   127  		return "", fmt.Errorf("Error importing container: %s\n\nStderr: %s", err, stderr.String())
   128  	}
   129  
   130  	return strings.TrimSpace(stdout.String()), nil
   131  }
   132  
   133  func (d *DockerDriver) IPAddress(id string) (string, error) {
   134  	var stderr, stdout bytes.Buffer
   135  	cmd := exec.Command(
   136  		"docker",
   137  		"inspect",
   138  		"--format",
   139  		"{{ .NetworkSettings.IPAddress }}",
   140  		id)
   141  	cmd.Stdout = &stdout
   142  	cmd.Stderr = &stderr
   143  	if err := cmd.Run(); err != nil {
   144  		return "", fmt.Errorf("Error: %s\n\nStderr: %s", err, stderr.String())
   145  	}
   146  
   147  	return strings.TrimSpace(stdout.String()), nil
   148  }
   149  
   150  func (d *DockerDriver) Login(repo, email, user, pass string) error {
   151  	d.l.Lock()
   152  
   153  	args := []string{"login"}
   154  	if email != "" {
   155  		args = append(args, "-e", email)
   156  	}
   157  	if user != "" {
   158  		args = append(args, "-u", user)
   159  	}
   160  	if pass != "" {
   161  		args = append(args, "-p", pass)
   162  	}
   163  	if repo != "" {
   164  		args = append(args, repo)
   165  	}
   166  
   167  	cmd := exec.Command("docker", args...)
   168  	err := runAndStream(cmd, d.Ui)
   169  	if err != nil {
   170  		d.l.Unlock()
   171  	}
   172  
   173  	return err
   174  }
   175  
   176  func (d *DockerDriver) Logout(repo string) error {
   177  	args := []string{"logout"}
   178  	if repo != "" {
   179  		args = append(args, repo)
   180  	}
   181  
   182  	cmd := exec.Command("docker", args...)
   183  	err := runAndStream(cmd, d.Ui)
   184  	d.l.Unlock()
   185  	return err
   186  }
   187  
   188  func (d *DockerDriver) Pull(image string) error {
   189  	cmd := exec.Command("docker", "pull", image)
   190  	return runAndStream(cmd, d.Ui)
   191  }
   192  
   193  func (d *DockerDriver) Push(name string) error {
   194  	cmd := exec.Command("docker", "push", name)
   195  	return runAndStream(cmd, d.Ui)
   196  }
   197  
   198  func (d *DockerDriver) SaveImage(id string, dst io.Writer) error {
   199  	var stderr bytes.Buffer
   200  	cmd := exec.Command("docker", "save", id)
   201  	cmd.Stdout = dst
   202  	cmd.Stderr = &stderr
   203  
   204  	log.Printf("Exporting image: %s", id)
   205  	if err := cmd.Start(); err != nil {
   206  		return err
   207  	}
   208  
   209  	if err := cmd.Wait(); err != nil {
   210  		err = fmt.Errorf("Error exporting: %s\nStderr: %s",
   211  			err, stderr.String())
   212  		return err
   213  	}
   214  
   215  	return nil
   216  }
   217  
   218  func (d *DockerDriver) StartContainer(config *ContainerConfig) (string, error) {
   219  	// Build up the template data
   220  	var tplData startContainerTemplate
   221  	tplData.Image = config.Image
   222  	ctx := *d.Ctx
   223  	ctx.Data = &tplData
   224  
   225  	// Args that we're going to pass to Docker
   226  	args := []string{"run"}
   227  	if config.Privileged {
   228  		args = append(args, "--privileged")
   229  	}
   230  	for host, guest := range config.Volumes {
   231  		if runtime.GOOS == "windows" {
   232  			// docker-toolbox can't handle the normal C:\filepath format in CLI
   233  			host = strings.Replace(host, "\\", "/", -1)
   234  			host = strings.Replace(host, "C:/", "/c/", 1)
   235  		}
   236  		args = append(args, "-v", fmt.Sprintf("%s:%s", host, guest))
   237  	}
   238  	for _, v := range config.RunCommand {
   239  		v, err := interpolate.Render(v, &ctx)
   240  		if err != nil {
   241  			return "", err
   242  		}
   243  
   244  		args = append(args, v)
   245  	}
   246  	d.Ui.Message(fmt.Sprintf(
   247  		"Run command: docker %s", strings.Join(args, " ")))
   248  
   249  	// Start the container
   250  	var stdout, stderr bytes.Buffer
   251  	cmd := exec.Command("docker", args...)
   252  	cmd.Stdout = &stdout
   253  	cmd.Stderr = &stderr
   254  
   255  	log.Printf("Starting container with args: %v", args)
   256  	if err := cmd.Start(); err != nil {
   257  		return "", err
   258  	}
   259  
   260  	log.Println("Waiting for container to finish starting")
   261  	if err := cmd.Wait(); err != nil {
   262  		if _, ok := err.(*exec.ExitError); ok {
   263  			err = fmt.Errorf("Docker exited with a non-zero exit status.\nStderr: %s",
   264  				stderr.String())
   265  		}
   266  
   267  		return "", err
   268  	}
   269  
   270  	// Capture the container ID, which is alone on stdout
   271  	return strings.TrimSpace(stdout.String()), nil
   272  }
   273  
   274  func (d *DockerDriver) StopContainer(id string) error {
   275  	if err := exec.Command("docker", "kill", id).Run(); err != nil {
   276  		return err
   277  	}
   278  
   279  	return exec.Command("docker", "rm", id).Run()
   280  }
   281  
   282  func (d *DockerDriver) TagImage(id string, repo string, force bool) error {
   283  	args := []string{"tag"}
   284  
   285  	// detect running docker version before tagging
   286  	// flag `force` for docker tagging was removed after Docker 1.12.0
   287  	// to keep its backward compatibility, we are not going to remove `force`
   288  	// option, but to ignore it when Docker version >= 1.12.0
   289  	//
   290  	// for more detail, please refer to the following links:
   291  	// - https://docs.docker.com/engine/deprecated/#/f-flag-on-docker-tag
   292  	// - https://github.com/docker/docker/pull/23090
   293  	version_running, err := d.Version()
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	version_deprecated, err := version.NewVersion(string("1.12.0"))
   299  	if err != nil {
   300  		// should never reach this line
   301  		return err
   302  	}
   303  
   304  	if force {
   305  		if version_running.LessThan(version_deprecated) {
   306  			args = append(args, "-f")
   307  		} else {
   308  			// do nothing if Docker version >= 1.12.0
   309  			log.Printf("[WARN] option: \"force\" will be ignored here")
   310  			log.Printf("since it was removed after Docker 1.12.0 released")
   311  		}
   312  	}
   313  	args = append(args, id, repo)
   314  
   315  	var stderr bytes.Buffer
   316  	cmd := exec.Command("docker", args...)
   317  	cmd.Stderr = &stderr
   318  
   319  	if err := cmd.Start(); err != nil {
   320  		return err
   321  	}
   322  
   323  	if err := cmd.Wait(); err != nil {
   324  		err = fmt.Errorf("Error tagging image: %s\nStderr: %s",
   325  			err, stderr.String())
   326  		return err
   327  	}
   328  
   329  	return nil
   330  }
   331  
   332  func (d *DockerDriver) Verify() error {
   333  	if _, err := exec.LookPath("docker"); err != nil {
   334  		return err
   335  	}
   336  
   337  	return nil
   338  }
   339  
   340  func (d *DockerDriver) Version() (*version.Version, error) {
   341  	output, err := exec.Command("docker", "-v").Output()
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  
   346  	match := regexp.MustCompile(version.VersionRegexpRaw).FindSubmatch(output)
   347  	if match == nil {
   348  		return nil, fmt.Errorf("unknown version: %s", output)
   349  	}
   350  
   351  	return version.NewVersion(string(match[0]))
   352  }