builder/docker: pull images
This commit is contained in:
parent
f9f10ed512
commit
034e04cc1e
|
@ -36,11 +36,15 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
|
||||
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
||||
steps := []multistep.Step{}
|
||||
steps := []multistep.Step{
|
||||
&StepPull{},
|
||||
}
|
||||
|
||||
// Setup the state bag and initial state for the steps
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", &b.config)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
|
||||
// Run!
|
||||
if b.config.PackerDebug {
|
||||
|
|
|
@ -8,5 +8,7 @@ import (
|
|||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
Image string
|
||||
|
||||
tpl *packer.ConfigTemplate
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/iochan"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"io"
|
||||
"log"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func runAndStream(cmd *exec.Cmd, ui packer.Ui) error {
|
||||
stdout_r, stdout_w := io.Pipe()
|
||||
stderr_r, stderr_w := io.Pipe()
|
||||
defer stdout_w.Close()
|
||||
defer stderr_w.Close()
|
||||
|
||||
log.Printf("Executing: %s %v", cmd.Path, cmd.Args[1:])
|
||||
cmd.Stdout = stdout_w
|
||||
cmd.Stderr = stderr_w
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the channels we'll use for data
|
||||
exitCh := make(chan int, 1)
|
||||
stdoutCh := iochan.DelimReader(stdout_r, '\n')
|
||||
stderrCh := iochan.DelimReader(stderr_r, '\n')
|
||||
|
||||
// Start the goroutine to watch for the exit
|
||||
go func() {
|
||||
defer stdout_w.Close()
|
||||
defer stderr_w.Close()
|
||||
exitStatus := 0
|
||||
|
||||
err := cmd.Wait()
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
exitStatus = 1
|
||||
|
||||
// There is no process-independent way to get the REAL
|
||||
// exit status so we just try to go deeper.
|
||||
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = status.ExitStatus()
|
||||
}
|
||||
}
|
||||
|
||||
exitCh <- exitStatus
|
||||
}()
|
||||
|
||||
// This waitgroup waits for the streaming to end
|
||||
var streamWg sync.WaitGroup
|
||||
streamWg.Add(2)
|
||||
|
||||
streamFunc := func(ch <-chan string) {
|
||||
defer streamWg.Done()
|
||||
|
||||
for data := range ch {
|
||||
data = cleanOutputLine(data)
|
||||
if data != "" {
|
||||
ui.Message(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stream stderr/stdout
|
||||
go streamFunc(stderrCh)
|
||||
go streamFunc(stdoutCh)
|
||||
|
||||
// Wait for the process to end and then wait for the streaming to end
|
||||
exitStatus := <-exitCh
|
||||
streamWg.Wait()
|
||||
|
||||
if exitStatus != 0 {
|
||||
return fmt.Errorf("Bad exit status: %d", exitStatus)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanOutputLine cleans up a line so that '\r' don't muck up the
|
||||
// UI output when we're reading from a remote command.
|
||||
func cleanOutputLine(line string) string {
|
||||
// Build a regular expression that will get rid of shell codes
|
||||
re := regexp.MustCompile("(?i)\x1b\\[([0-9]{1,2}(;[0-9]{1,2})?)?[a|b|m|k]")
|
||||
line = re.ReplaceAllString(line, "")
|
||||
|
||||
// Trim surrounding whitespace
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// Trim up to the first carriage return, since that text would be
|
||||
// lost anyways.
|
||||
idx := strings.LastIndex(line, "\r")
|
||||
if idx > -1 {
|
||||
line = line[idx+1:]
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type StepPull struct{}
|
||||
|
||||
func (s *StepPull) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say(fmt.Sprintf("Pulling Docker image: %s", config.Image))
|
||||
cmd := exec.Command("docker", "pull", config.Image)
|
||||
if err := runAndStream(cmd, ui); err != nil {
|
||||
err := fmt.Errorf("Error pulling Docker image: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepPull) Cleanup(state multistep.StateBag) {
|
||||
}
|
Loading…
Reference in New Issue