func (c *Communicator) Start(cmd *packer.RemoteCmd) error { localCmd := exec.Command("sh", "-c", cmd.Command) localCmd.Stdin = cmd.Stdin localCmd.Stdout = cmd.Stdout localCmd.Stderr = cmd.Stderr // Start it. If it doesn't work, then error right away. if err := localCmd.Start(); err != nil { return err } // We've started successfully. Start a goroutine to wait for // it to complete and track exit status. go func() { var exitStatus int err := localCmd.Wait() if err != nil { 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() } } } cmd.SetExited(exitStatus) }() return nil }
func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) { defer shell.Close() var wg sync.WaitGroup copyFunc := func(w io.Writer, r io.Reader) { defer wg.Done() io.Copy(w, r) } if rc.Stdout != nil && cmd.Stdout != nil { wg.Add(1) go copyFunc(rc.Stdout, cmd.Stdout) } else { log.Printf("[WARN] Failed to read stdout for command '%s'", rc.Command) } if rc.Stderr != nil && cmd.Stderr != nil { wg.Add(1) go copyFunc(rc.Stderr, cmd.Stderr) } else { log.Printf("[WARN] Failed to read stderr for command '%s'", rc.Command) } cmd.Wait() wg.Wait() code := cmd.ExitCode() log.Printf("[INFO] command '%s' exited with code: %d", rc.Command, code) rc.SetExited(code) }
// Runs the given command and blocks until completion func (c *Communicator) run(cmd *exec.Cmd, remote *packer.RemoteCmd, stdin io.WriteCloser, stdout, stderr io.ReadCloser) { // For Docker, remote communication must be serialized since it // only supports single execution. c.lock.Lock() defer c.lock.Unlock() wg := sync.WaitGroup{} repeat := func(w io.Writer, r io.ReadCloser) { io.Copy(w, r) r.Close() wg.Done() } if remote.Stdout != nil { wg.Add(1) go repeat(remote.Stdout, stdout) } if remote.Stderr != nil { wg.Add(1) go repeat(remote.Stderr, stderr) } // Start the command log.Printf("Executing %s:", strings.Join(cmd.Args, " ")) if err := cmd.Start(); err != nil { log.Printf("Error executing: %s", err) remote.SetExited(254) return } var exitStatus int if remote.Stdin != nil { go func() { io.Copy(stdin, remote.Stdin) // close stdin to support commands that wait for stdin to be closed before exiting. stdin.Close() }() } wg.Wait() 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() } } // Set the exit status which triggers waiters remote.SetExited(exitStatus) }
func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) { defer shell.Close() go io.Copy(rc.Stdout, cmd.Stdout) go io.Copy(rc.Stderr, cmd.Stderr) cmd.Wait() rc.SetExited(cmd.ExitCode()) }
func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { session, err := c.newSession() if err != nil { return } // Setup our session session.Stdin = cmd.Stdin session.Stdout = cmd.Stdout session.Stderr = cmd.Stderr if c.config.Pty { // Request a PTY termModes := ssh.TerminalModes{ ssh.ECHO: 0, // do not echo ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud } if err = session.RequestPty("xterm", 40, 80, termModes); err != nil { return } } log.Printf("starting remote command: %s", cmd.Command) err = session.Start(cmd.Command + "\n") if err != nil { return } // Start a goroutine to wait for the session to end and set the // exit boolean and status. go func() { defer session.Close() err := session.Wait() exitStatus := 0 if err != nil { switch err.(type) { case *ssh.ExitError: exitStatus = err.(*ssh.ExitError).ExitStatus() log.Printf("Remote command exited with '%d': %s", exitStatus, cmd.Command) case *ssh.ExitMissingError: log.Printf("Remote command exited without exit status or exit signal.") exitStatus = -1 default: log.Printf("Error occurred waiting for ssh session: %s", err.Error()) exitStatus = -1 } } cmd.SetExited(exitStatus) }() return }
func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) { defer shell.Close() go io.Copy(rc.Stdout, cmd.Stdout) go io.Copy(rc.Stderr, cmd.Stderr) cmd.Wait() code := cmd.ExitCode() log.Printf("[INFO] command '%s' exited with code: %d", rc.Command, code) rc.SetExited(code) }
func (c *communicator) Start(cmd *packer.RemoteCmd) (err error) { var args CommunicatorStartArgs args.Command = cmd.Command if cmd.Stdin != nil { stdinL := netListenerInRange(portRangeMin, portRangeMax) args.StdinAddress = stdinL.Addr().String() go serveSingleCopy("stdin", stdinL, nil, cmd.Stdin) } if cmd.Stdout != nil { stdoutL := netListenerInRange(portRangeMin, portRangeMax) args.StdoutAddress = stdoutL.Addr().String() go serveSingleCopy("stdout", stdoutL, cmd.Stdout, nil) } if cmd.Stderr != nil { stderrL := netListenerInRange(portRangeMin, portRangeMax) args.StderrAddress = stderrL.Addr().String() go serveSingleCopy("stderr", stderrL, cmd.Stderr, nil) } responseL := netListenerInRange(portRangeMin, portRangeMax) args.ResponseAddress = responseL.Addr().String() go func() { defer responseL.Close() conn, err := responseL.Accept() if err != nil { log.Panic(err) } defer conn.Close() decoder := gob.NewDecoder(conn) var finished CommandFinished if err := decoder.Decode(&finished); err != nil { log.Panic(err) } cmd.SetExited(finished.ExitStatus) }() err = c.client.Call("Communicator.Start", &args, new(interface{})) return }
func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { session, err := c.newSession() if err != nil { return } // Setup our session session.Stdin = cmd.Stdin session.Stdout = cmd.Stdout session.Stderr = cmd.Stderr // Request a PTY termModes := ssh.TerminalModes{ ssh.ECHO: 0, // do not echo ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud } if err = session.RequestPty("xterm", 80, 40, termModes); err != nil { return } log.Printf("starting remote command: %s", cmd.Command) err = session.Start(cmd.Command + "\n") if err != nil { return } // Start a goroutine to wait for the session to end and set the // exit boolean and status. go func() { defer session.Close() err := session.Wait() exitStatus := 0 if err != nil { exitErr, ok := err.(*ssh.ExitError) if ok { exitStatus = exitErr.ExitStatus() } } log.Printf("remote command exited with '%d': %s", exitStatus, cmd.Command) cmd.SetExited(exitStatus) }() return }
func (c *Communicator) Start(cmd *packer.RemoteCmd) error { // Render the template so that we know how to execute the command c.Ctx.Data = &ExecuteCommandTemplate{ Command: cmd.Command, } for i, field := range c.ExecuteCommand { command, err := interpolate.Render(field, &c.Ctx) if err != nil { return fmt.Errorf("Error processing command: %s", err) } c.ExecuteCommand[i] = command } // Build the local command to execute localCmd := exec.Command(c.ExecuteCommand[0], c.ExecuteCommand[1:]...) localCmd.Stdin = cmd.Stdin localCmd.Stdout = cmd.Stdout localCmd.Stderr = cmd.Stderr // Start it. If it doesn't work, then error right away. if err := localCmd.Start(); err != nil { return err } // We've started successfully. Start a goroutine to wait for // it to complete and track exit status. go func() { var exitStatus int err := localCmd.Wait() if err != nil { 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() } } } cmd.SetExited(exitStatus) }() return nil }
func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) { defer shell.Close() if rc.Stdout != nil && cmd.Stdout != nil { go io.Copy(rc.Stdout, cmd.Stdout) } else { log.Printf("[WARN] Failed to read stdout for command '%s'", rc.Command) } if rc.Stderr != nil && cmd.Stderr != nil { go io.Copy(rc.Stderr, cmd.Stderr) } else { log.Printf("[WARN] Failed to read stderr for command '%s'", rc.Command) } cmd.Wait() code := cmd.ExitCode() log.Printf("[INFO] command '%s' exited with code: %d", rc.Command, code) rc.SetExited(code) }
func (c *Communicator) Start(cmd *packer.RemoteCmd) error { command, err := c.CmdWrapper( fmt.Sprintf("sudo chroot %s '%s'", c.Chroot, cmd.Command)) if err != nil { return err } localCmd := ShellCommand(command) localCmd.Stdin = cmd.Stdin localCmd.Stdout = cmd.Stdout localCmd.Stderr = cmd.Stderr log.Printf("Executing: %s %#v", localCmd.Path, localCmd.Args) if err := localCmd.Start(); err != nil { return err } go func() { exitStatus := 0 if err := localCmd.Wait(); err != nil { 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() } } } log.Printf( "Chroot executation exited with '%d': '%s'", exitStatus, cmd.Command) cmd.SetExited(exitStatus) }() return nil }
func (c *LxcAttachCommunicator) Start(cmd *packer.RemoteCmd) error { localCmd, err := c.Execute(cmd.Command) if err != nil { return err } localCmd.Stdin = cmd.Stdin localCmd.Stdout = cmd.Stdout localCmd.Stderr = cmd.Stderr if err := localCmd.Start(); err != nil { return err } go func() { exitStatus := 0 if err := localCmd.Wait(); err != nil { 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() } } } log.Printf( "lxc-attach execution exited with '%d': '%s'", exitStatus, cmd.Command) cmd.SetExited(exitStatus) }() return nil }
func (c *communicator) Start(cmd *packer.RemoteCmd) (err error) { var args CommunicatorStartArgs args.Command = cmd.Command if cmd.Stdin != nil { args.StdinStreamId = c.mux.NextId() go serveSingleCopy("stdin", c.mux, args.StdinStreamId, nil, cmd.Stdin) } if cmd.Stdout != nil { args.StdoutStreamId = c.mux.NextId() go serveSingleCopy("stdout", c.mux, args.StdoutStreamId, cmd.Stdout, nil) } if cmd.Stderr != nil { args.StderrStreamId = c.mux.NextId() go serveSingleCopy("stderr", c.mux, args.StderrStreamId, cmd.Stderr, nil) } responseStreamId := c.mux.NextId() args.ResponseStreamId = responseStreamId go func() { conn, err := c.mux.Accept(responseStreamId) if err != nil { log.Printf("[ERR] Error accepting response stream %d: %s", responseStreamId, err) cmd.SetExited(123) return } defer conn.Close() var finished CommandFinished decoder := gob.NewDecoder(conn) if err := decoder.Decode(&finished); err != nil { log.Printf("[ERR] Error decoding response stream %d: %s", responseStreamId, err) cmd.SetExited(123) return } log.Printf("[INFO] RPC client: Communicator ended with: %d", finished.ExitStatus) cmd.SetExited(finished.ExitStatus) }() err = c.client.Call("Communicator.Start", &args, new(interface{})) return }
func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { session, err := c.newSession() if err != nil { return } // Setup our session session.Stdin = cmd.Stdin session.Stdout = cmd.Stdout session.Stderr = cmd.Stderr if !c.config.NoPty { // Request a PTY termModes := ssh.TerminalModes{ ssh.ECHO: 0, // do not echo ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud } if err = session.RequestPty("xterm", 80, 40, termModes); err != nil { return } } log.Printf("starting remote command: %s", cmd.Command) err = session.Start(cmd.Command + "\n") if err != nil { return } // A channel to keep track of our done state doneCh := make(chan struct{}) sessionLock := new(sync.Mutex) timedOut := false // Start a goroutine to wait for the session to end and set the // exit boolean and status. go func() { defer session.Close() err := session.Wait() exitStatus := 0 if err != nil { exitErr, ok := err.(*ssh.ExitError) if ok { exitStatus = exitErr.ExitStatus() } } sessionLock.Lock() defer sessionLock.Unlock() if timedOut { // We timed out, so set the exit status to -1 exitStatus = -1 } log.Printf("remote command exited with '%d': %s", exitStatus, cmd.Command) cmd.SetExited(exitStatus) close(doneCh) }() go func() { failures := 0 for { dummy, err := c.config.Connection() if err == nil { failures = 0 dummy.Close() } select { case <-doneCh: return default: } if err != nil { log.Printf("background SSH connection checker failure: %s", err) failures += 1 } if failures < 5 { time.Sleep(5 * time.Second) continue } // Acquire a lock in order to modify session state sessionLock.Lock() defer sessionLock.Unlock() // Kill the connection and mark that we timed out. log.Printf("Too many SSH connection failures. Killing it!") c.conn.Close() timedOut = true return } }() return }
func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { cmd.SetExited(0) return }
func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { session, err := c.newSession() if err != nil { return } // Setup our session session.Stdin = cmd.Stdin session.Stdout = cmd.Stdout session.Stderr = cmd.Stderr if c.config.Pty { // Request a PTY termModes := ssh.TerminalModes{ ssh.ECHO: 0, // do not echo ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud } if err = session.RequestPty("xterm", 40, 80, termModes); err != nil { return } } log.Printf("starting remote command: %s", cmd.Command) err = session.Start(cmd.Command + "\n") if err != nil { return } // A channel to keep track of our done state doneCh := make(chan struct{}) sessionLock := new(sync.Mutex) timedOut := false // Start a goroutine to wait for the session to end and set the // exit boolean and status. go func() { defer session.Close() err := session.Wait() exitStatus := 0 if err != nil { exitErr, ok := err.(*ssh.ExitError) if ok { exitStatus = exitErr.ExitStatus() } } sessionLock.Lock() defer sessionLock.Unlock() if timedOut { // We timed out, so set the exit status to -1 exitStatus = -1 } log.Printf("remote command exited with '%d': %s", exitStatus, cmd.Command) cmd.SetExited(exitStatus) close(doneCh) }() return }
// Runs the given command and blocks until completion func (c *Communicator) run(cmd *exec.Cmd, remote *packer.RemoteCmd, stdin_w io.WriteCloser, outputFile *os.File, exitCodePath string) { // For Docker, remote communication must be serialized since it // only supports single execution. c.lock.Lock() defer c.lock.Unlock() // Clean up after ourselves by removing our temporary files defer os.Remove(outputFile.Name()) defer os.Remove(exitCodePath) // Tail the output file and send the data to the stdout listener tail, err := tail.TailFile(outputFile.Name(), tail.Config{ Poll: true, ReOpen: true, Follow: true, }) if err != nil { log.Printf("Error tailing output file: %s", err) remote.SetExited(254) return } defer tail.Stop() // Modify the remote command so that all the output of the commands // go to a single file and so that the exit code is redirected to // a single file. This lets us determine both when the command // is truly complete (because the file will have data), what the // exit status is (because Docker loses it because of the pty, not // Docker's fault), and get the output (Docker bug). remoteCmd := fmt.Sprintf("(%s) >%s 2>&1; echo $? >%s", remote.Command, filepath.Join(c.ContainerDir, filepath.Base(outputFile.Name())), filepath.Join(c.ContainerDir, filepath.Base(exitCodePath))) // Start the command log.Printf("Executing in container %s: %#v", c.ContainerId, remoteCmd) if err := cmd.Start(); err != nil { log.Printf("Error executing: %s", err) remote.SetExited(254) return } go func() { defer stdin_w.Close() // This sleep needs to be here because of the issue linked to below. // Basically, without it, Docker will hang on reading stdin forever, // and won't see what we write, for some reason. // // https://github.com/dotcloud/docker/issues/2628 time.Sleep(2 * time.Second) stdin_w.Write([]byte(remoteCmd + "\n")) }() // Start a goroutine to read all the lines out of the logs. These channels // allow us to stop the go-routine and wait for it to be stopped. stopTailCh := make(chan struct{}) doneCh := make(chan struct{}) go func() { defer close(doneCh) for { select { case <-tail.Dead(): return case line := <-tail.Lines: if remote.Stdout != nil { remote.Stdout.Write([]byte(line.Text + "\n")) } else { log.Printf("Command stdout: %#v", line.Text) } case <-time.After(2 * time.Second): // If we're done, then return. Otherwise, keep grabbing // data. This gives us a chance to flush all the lines // out of the tailed file. select { case <-stopTailCh: return default: } } } }() var exitRaw []byte var exitStatus int var exitStatusRaw int64 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() } // Say that we ended, since if Docker itself failed, then // the command must've not run, or so we assume goto REMOTE_EXIT } // Wait for the exit code to appear in our file... log.Println("Waiting for exit code to appear for remote command...") for { fi, err := os.Stat(exitCodePath) if err == nil && fi.Size() > 0 { break } time.Sleep(1 * time.Second) } // Read the exit code exitRaw, err = ioutil.ReadFile(exitCodePath) if err != nil { log.Printf("Error executing: %s", err) exitStatus = 254 goto REMOTE_EXIT } exitStatusRaw, err = strconv.ParseInt(string(bytes.TrimSpace(exitRaw)), 10, 0) if err != nil { log.Printf("Error executing: %s", err) exitStatus = 254 goto REMOTE_EXIT } exitStatus = int(exitStatusRaw) log.Printf("Executed command exit status: %d", exitStatus) REMOTE_EXIT: // Wait for the tail to finish close(stopTailCh) <-doneCh // Set the exit status which triggers waiters remote.SetExited(exitStatus) }