func executeCommand(command string, comm packer.Communicator) (err error) { // Setup the remote command stdout_r, stdout_w := io.Pipe() stderr_r, stderr_w := io.Pipe() var cmd packer.RemoteCmd cmd.Command = command cmd.Stdout = stdout_w cmd.Stderr = stderr_w log.Printf("Executing command: %s", cmd.Command) err = comm.Start(&cmd) if err != nil { return fmt.Errorf("Failed executing command: %s", err) } exitChan := make(chan int, 1) stdoutChan := iochan.DelimReader(stdout_r, '\n') stderrChan := iochan.DelimReader(stderr_r, '\n') go func() { defer stdout_w.Close() defer stderr_w.Close() cmd.Wait() exitChan <- cmd.ExitStatus }() OutputLoop: for { select { case output := <-stderrChan: Ui.Message(strings.TrimSpace(output)) case output := <-stdoutChan: Ui.Message(strings.TrimSpace(output)) case exitStatus := <-exitChan: log.Printf("Puppet provisioner exited with status %d", exitStatus) if exitStatus != 0 { return fmt.Errorf("Command exited with non-zero exit status: %d", exitStatus) } break OutputLoop } } // Make sure we finish off stdout/stderr because we may have gotten // a message from the exit channel first. for output := range stdoutChan { Ui.Message(output) } for output := range stderrChan { Ui.Message(output) } return nil }
func buildToolchain(wg *sync.WaitGroup, semaphore chan int, root string, platform Platform, verbose bool) error { defer wg.Done() semaphore <- 1 defer func() { <-semaphore }() fmt.Printf("--> Toolchain: %s\n", platform.String()) scriptName := "make.bash" if runtime.GOOS == "windows" { scriptName = "make.bat" } var stderr bytes.Buffer var stdout bytes.Buffer scriptDir := filepath.Join(root, "src") scriptPath := filepath.Join(scriptDir, scriptName) cmd := exec.Command(scriptPath, "--no-clean") cmd.Dir = scriptDir cmd.Env = append(os.Environ(), "GOARCH="+platform.Arch, "GOOS="+platform.OS) cmd.Stderr = &stderr cmd.Stdout = &stdout if verbose { // In verbose mode, we output all stdout to the console. r, w := io.Pipe() cmd.Stdout = w cmd.Stderr = io.MultiWriter(cmd.Stderr, w) // Send all the output to stdout, and also make a done channel // so that this compilation isn't done until we receive all output doneCh := make(chan struct{}) go func() { defer close(doneCh) for line := range iochan.DelimReader(r, '\n') { fmt.Printf("%s: %s", platform.String(), line) } }() defer func() { w.Close() <-doneCh }() } if err := cmd.Start(); err != nil { return fmt.Errorf("Error building '%s': %s", platform.String(), err) } if err := cmd.Wait(); err != nil { return fmt.Errorf("Error building '%s'.\n\nStdout: %s\n\nStderr: %s\n", platform.String(), stdout.String(), stderr.String()) } return nil }
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { scripts := make([]string, len(p.config.Scripts)) copy(scripts, p.config.Scripts) // If we have an inline script, then turn that into a temporary // shell script and use that. if p.config.Inline != nil { tf, err := ioutil.TempFile("", "packer-shell") if err != nil { return fmt.Errorf("Error preparing shell script: %s", err) } defer os.Remove(tf.Name()) // Set the path to the temporary file scripts = append(scripts, tf.Name()) // Write our contents to it writer := bufio.NewWriter(tf) writer.WriteString(fmt.Sprintf("#!%s\n", p.config.InlineShebang)) for _, command := range p.config.Inline { if _, err := writer.WriteString(command + "\n"); err != nil { return fmt.Errorf("Error preparing shell script: %s", err) } } if err := writer.Flush(); err != nil { return fmt.Errorf("Error preparing shell script: %s", err) } tf.Close() } for _, path := range scripts { ui.Say(fmt.Sprintf("Provisioning with shell script: %s", path)) log.Printf("Opening %s for reading", path) f, err := os.Open(path) if err != nil { return fmt.Errorf("Error opening shell script: %s", err) } defer f.Close() log.Printf("Uploading %s => %s", path, p.config.RemotePath) err = comm.Upload(p.config.RemotePath, f) if err != nil { return fmt.Errorf("Error uploading shell script: %s", err) } // Close the original file since we copied it f.Close() // Flatten the environment variables flattendVars := strings.Join(p.config.Vars, " ") // Compile the command var command bytes.Buffer t := template.Must(template.New("command").Parse(p.config.ExecuteCommand)) t.Execute(&command, &ExecuteCommandTemplate{flattendVars, p.config.RemotePath}) // Setup the remote command stdout_r, stdout_w := io.Pipe() stderr_r, stderr_w := io.Pipe() var cmd packer.RemoteCmd cmd.Command = command.String() cmd.Stdout = stdout_w cmd.Stderr = stderr_w log.Printf("Executing command: %s", cmd.Command) err = comm.Start(&cmd) if err != nil { return fmt.Errorf("Failed executing command: %s", err) } exitChan := make(chan int, 1) stdoutChan := iochan.DelimReader(stdout_r, '\n') stderrChan := iochan.DelimReader(stderr_r, '\n') go func() { defer stdout_w.Close() defer stderr_w.Close() cmd.Wait() exitChan <- cmd.ExitStatus }() OutputLoop: for { select { case output := <-stderrChan: ui.Message(strings.TrimSpace(output)) case output := <-stdoutChan: ui.Message(strings.TrimSpace(output)) case exitStatus := <-exitChan: log.Printf("shell provisioner exited with status %d", exitStatus) if exitStatus != 0 { return fmt.Errorf("Script exited with non-zero exit status: %d", exitStatus) } break OutputLoop } } // Make sure we finish off stdout/stderr because we may have gotten // a message from the exit channel first. for output := range stdoutChan { ui.Message(output) } for output := range stderrChan { ui.Message(output) } } return nil }
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 }
// StartWithUi runs the remote command and streams the output to any // configured Writers for stdout/stderr, while also writing each line // as it comes to a Ui. func (r *RemoteCmd) StartWithUi(c Communicator, ui Ui) error { stdout_r, stdout_w := io.Pipe() stderr_r, stderr_w := io.Pipe() defer stdout_w.Close() defer stderr_w.Close() // Retain the original stdout/stderr that we can replace back in. originalStdout := r.Stdout originalStderr := r.Stderr defer func() { r.Lock() defer r.Unlock() r.Stdout = originalStdout r.Stderr = originalStderr }() // Set the writers for the output so that we get it streamed to us if r.Stdout == nil { r.Stdout = stdout_w } else { r.Stdout = io.MultiWriter(r.Stdout, stdout_w) } if r.Stderr == nil { r.Stderr = stderr_w } else { r.Stderr = io.MultiWriter(r.Stderr, stderr_w) } // Start the command if err := c.Start(r); err != nil { return err } // Create the channels we'll use for data exitCh := make(chan struct{}) stdoutCh := iochan.DelimReader(stdout_r, '\n') stderrCh := iochan.DelimReader(stderr_r, '\n') // Start the goroutine to watch for the exit go func() { defer close(exitCh) defer stdout_w.Close() defer stderr_w.Close() r.Wait() }() // Loop and get all our output OutputLoop: for { select { case output := <-stderrCh: if output != "" { ui.Message(r.cleanOutputLine(output)) } case output := <-stdoutCh: if output != "" { ui.Message(r.cleanOutputLine(output)) } case <-exitCh: break OutputLoop } } // Make sure we finish off stdout/stderr because we may have gotten // a message from the exit channel before finishing these first. for output := range stdoutCh { ui.Message(strings.TrimSpace(output)) } for output := range stderrCh { ui.Message(strings.TrimSpace(output)) } return nil }
// StartWithUi runs the remote command and streams the output to any // configured Writers for stdout/stderr, while also writing each line // as it comes to a Ui. func (r *RemoteCmd) StartWithUi(c Communicator, ui Ui) error { stdout_r, stdout_w := io.Pipe() stderr_r, stderr_w := io.Pipe() defer stdout_w.Close() defer stderr_w.Close() // Set the writers for the output so that we get it streamed to us if r.Stdout == nil { r.Stdout = stdout_w } else { r.Stdout = io.MultiWriter(r.Stdout, stdout_w) } if r.Stderr == nil { r.Stderr = stderr_w } else { r.Stderr = io.MultiWriter(r.Stderr, stderr_w) } // Start the command if err := c.Start(r); 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() r.Wait() exitCh <- r.ExitStatus }() // Loop and get all our output OutputLoop: for { select { case output := <-stderrCh: ui.Message(strings.TrimSpace(output)) case output := <-stdoutCh: ui.Message(strings.TrimSpace(output)) case <-exitCh: break OutputLoop } } // Make sure we finish off stdout/stderr because we may have gotten // a message from the exit channel before finishing these first. for output := range stdoutCh { ui.Message(strings.TrimSpace(output)) } for output := range stderrCh { ui.Message(strings.TrimSpace(output)) } return nil }