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 }