func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, localCookbooks []string) (string, error) { ui.Message("Creating configuration file 'solo.rb'") cookbook_paths := make([]string, len(p.config.RemoteCookbookPaths)+len(localCookbooks)) for i, path := range p.config.RemoteCookbookPaths { cookbook_paths[i] = fmt.Sprintf(`"%s"`, path) } for i, path := range localCookbooks { i = len(p.config.RemoteCookbookPaths) + i cookbook_paths[i] = fmt.Sprintf(`"%s"`, path) } configString, err := p.config.tpl.Process(DefaultConfigTemplate, &ConfigTemplate{ CookbookPaths: strings.Join(cookbook_paths, ","), }) if err != nil { return "", err } remotePath := filepath.Join(p.config.StagingDir, "solo.rb") if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString))); err != nil { return "", err } return remotePath, nil }
func (p *Provisioner) createKnifeConfig(ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, clientKey string, sslVerifyMode string) (string, error) { ui.Message("Creating configuration file 'knife.rb'") // Read the template tpl := DefaultKnifeTemplate ctx := p.config.ctx ctx.Data = &ConfigTemplate{ NodeName: nodeName, ServerUrl: serverUrl, ClientKey: clientKey, SslVerifyMode: sslVerifyMode, } configString, err := interpolate.Render(tpl, &ctx) if err != nil { return "", err } remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "knife.rb")) if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil { return "", err } return remotePath, nil }
func (p *Provisioner) uploadDeploymentManifest(ui packer.Ui, comm packer.Communicator) error { ui.Say(fmt.Sprintf("Uploading manifest: %s", p.config.Manifest)) err := runCmd("mkdir ~/deployments", ui, comm) if err != nil { return err } f, err := os.Open(p.config.Manifest) if err != nil { return err } defer f.Close() fi, err := f.Stat() if err != nil { return err } err = comm.Upload(fmt.Sprintf("~/deployments/%s", p.config.Manifest), f, &fi) if err != nil { return err } cmd := fmt.Sprintf("sed -i \"s/director_uuid: .*/director_uuid: $(bosh status --uuid)/\" ~/deployments/%s", p.config.Manifest) return runCmd(cmd, ui, comm) }
func (p *Provisioner) ProvisionUpload(ui packer.Ui, comm packer.Communicator) error { ui.Say(fmt.Sprintf("Uploading %s => %s", p.config.Source, p.config.Destination)) info, err := os.Stat(p.config.Source) if err != nil { return err } // If we're uploading a directory, short circuit and do that if info.IsDir() { return comm.UploadDir(p.config.Destination, p.config.Source, nil) } // We're uploading a file... f, err := os.Open(p.config.Source) if err != nil { return err } defer f.Close() fi, err := f.Stat() if err != nil { return err } err = comm.Upload(p.config.Destination, f, &fi) if err != nil { ui.Error(fmt.Sprintf("Upload failed: %s", err)) } return err }
func (p *Provisioner) uploadManifests(ui packer.Ui, comm packer.Communicator) (string, error) { // Create the remote manifests directory... ui.Message("Uploading manifests...") remoteManifestsPath := fmt.Sprintf("%s/manifests", p.config.StagingDir) if err := p.createDir(ui, comm, remoteManifestsPath); err != nil { return "", fmt.Errorf("Error creating manifests directory: %s", err) } // Upload the main manifest f, err := os.Open(p.config.ManifestFile) if err != nil { return "", err } defer f.Close() manifestFilename := p.config.ManifestFile if fi, err := os.Stat(p.config.ManifestFile); err != nil { return "", fmt.Errorf("Error inspecting manifest file: %s", err) } else if !fi.IsDir() { manifestFilename = filepath.Base(manifestFilename) } else { ui.Say("WARNING: manifest_file should be a file. Use manifest_dir for directories") } remoteManifestFile := fmt.Sprintf("%s/%s", remoteManifestsPath, manifestFilename) if err := comm.Upload(remoteManifestFile, f, nil); err != nil { return "", err } return remoteManifestFile, nil }
func (p *Provisioner) ProvisionDownload(ui packer.Ui, comm packer.Communicator) error { for _, src := range p.config.Sources { ui.Say(fmt.Sprintf("Downloading %s => %s", src, p.config.Destination)) // ensure destination dir exists. p.config.Destination may either be a file or a dir. dir := p.config.Destination // if it doesn't end with a /, set dir as the parent dir if !strings.HasSuffix(p.config.Destination, "/") { dir = filepath.Dir(dir) } if dir != "" { err := os.MkdirAll(dir, os.FileMode(0755)) if err != nil { return err } } // if the config.Destination was a dir, download the dir if strings.HasSuffix(p.config.Destination, "/") { return comm.DownloadDir(src, p.config.Destination, nil) } f, err := os.OpenFile(p.config.Destination, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { return err } defer f.Close() err = comm.Download(src, f) if err != nil { ui.Error(fmt.Sprintf("Download failed: %s", err)) return err } } return nil }
func UploadLocalDirectory(localDir string, comm packer.Communicator) (err error) { visitPath := func(path string, f os.FileInfo, err error) (err2 error) { var remotePath = RemoteStagingPath + "/" + path if f.IsDir() { // Make remote directory err = CreateRemoteDirectory(remotePath, comm) if err != nil { return err } } else { // Upload file to existing directory file, err := os.Open(path) if err != nil { return fmt.Errorf("Error opening file: %s", err) } err = comm.Upload(remotePath, file) if err != nil { return fmt.Errorf("Error uploading file: %s", err) } } return } log.Printf("Uploading directory %s", localDir) err = filepath.Walk(localDir, visitPath) if err != nil { return fmt.Errorf("Error uploading modules %s: %s", localDir, err) } return nil }
func (p *Provisioner) ProvisionDownload(ui packer.Ui, comm packer.Communicator) error { for _, src := range p.config.Sources { ui.Say(fmt.Sprintf("Downloading %s => %s", src, p.config.Destination)) if strings.HasSuffix(p.config.Destination, "/") { err := os.MkdirAll(p.config.Destination, os.FileMode(0755)) if err != nil { return err } return comm.DownloadDir(src, p.config.Destination, nil) } f, err := os.OpenFile(p.config.Destination, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { return err } defer f.Close() err = comm.Download(src, f) if err != nil { ui.Error(fmt.Sprintf("Download failed: %s", err)) return err } } return nil }
func (p *Provisioner) createJson(ui packer.Ui, comm packer.Communicator) (string, error) { ui.Message("Creating JSON attribute file") jsonData := make(map[string]interface{}) // Copy the configured JSON for k, v := range p.config.Json { jsonData[k] = v } // Set the run list if it was specified if len(p.config.RunList) > 0 { jsonData["run_list"] = p.config.RunList } jsonBytes, err := json.MarshalIndent(jsonData, "", " ") if err != nil { return "", err } // Upload the bytes remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "node.json")) if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes), nil); err != nil { return "", err } return remotePath, nil }
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 (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst string, src string) error { f, err := os.Open(src) if err != nil { return err } defer f.Close() return comm.Upload(dst, f, nil) }
func (s *StepUploadX509Cert) uploadSingle(comm packer.Communicator, dst, src string) error { f, err := os.Open(src) if err != nil { return err } defer f.Close() return comm.Upload(dst, f, nil) }
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { ui.Say(fmt.Sprintf("Uploading %s => %s", p.config.Source, p.config.Destination)) f, err := os.Open(p.config.Source) if err != nil { return err } defer f.Close() return comm.Upload(p.config.Destination, f) }
func (p *Provisioner) ProvisionUpload(ui packer.Ui, comm packer.Communicator) error { ui.Say(fmt.Sprintf("Uploading %s => %s", p.config.Source, p.config.Destination)) info, _ := os.Stat(p.config.Source) if info != nil { // If we're uploading a directory, short circuit and do that if info.IsDir() { return comm.UploadDir(p.config.Destination, p.config.Source, nil) } } pwd, err := os.Getwd() if err != nil { return fmt.Errorf("Couldn't get the current working directory") } det, err := gg.Detect(p.config.Source, pwd, gg.Detectors) if err != nil { return fmt.Errorf("Couldn't detect file source type: %v", err) } if len(det) == 0 { return errors.New("Don't recognise the source type") } dir, err := ioutil.TempDir("", "packer") if err != nil { return errors.New("Unable to create temp dir") } defer os.RemoveAll(dir) source := filepath.Join(dir, filepath.Base(p.config.Source)) if err := gg.GetFile(source, p.config.Source); err != nil { return fmt.Errorf("There was a problem getting the file: %v", err) } // We're uploading a file... f, err := os.Open(source) if err != nil { return err } defer f.Close() fi, err := f.Stat() if err != nil { return err } err = comm.Upload(p.config.Destination, f, &fi) if err != nil { ui.Error(fmt.Sprintf("Upload failed: %s", err)) } return err }
func (p *Provisioner) createConfig( ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, clientKey string, encryptedDataBagSecretPath, remoteKeyPath string, validationClientName string, chefEnvironment string, sslVerifyMode string) (string, error) { ui.Message("Creating configuration file 'client.rb'") // Read the template tpl := DefaultConfigTemplate if p.config.ConfigTemplate != "" { f, err := os.Open(p.config.ConfigTemplate) if err != nil { return "", err } defer f.Close() tplBytes, err := ioutil.ReadAll(f) if err != nil { return "", err } tpl = string(tplBytes) } ctx := p.config.ctx ctx.Data = &ConfigTemplate{ NodeName: nodeName, ServerUrl: serverUrl, ClientKey: clientKey, ValidationKeyPath: remoteKeyPath, ValidationClientName: validationClientName, ChefEnvironment: chefEnvironment, SslVerifyMode: sslVerifyMode, EncryptedDataBagSecretPath: encryptedDataBagSecretPath, } configString, err := interpolate.Render(tpl, &ctx) if err != nil { return "", err } remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "client.rb")) if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil { return "", err } return remotePath, nil }
func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, localCookbooks []string, rolesPath string, dataBagsPath string, encryptedDataBagSecretPath string, environmentsPath string, chefEnvironment string) (string, error) { ui.Message("Creating configuration file 'solo.rb'") cookbook_paths := make([]string, len(p.config.RemoteCookbookPaths)+len(localCookbooks)) for i, path := range p.config.RemoteCookbookPaths { cookbook_paths[i] = fmt.Sprintf(`"%s"`, path) } for i, path := range localCookbooks { i = len(p.config.RemoteCookbookPaths) + i cookbook_paths[i] = fmt.Sprintf(`"%s"`, path) } // Read the template tpl := DefaultConfigTemplate if p.config.ConfigTemplate != "" { f, err := os.Open(p.config.ConfigTemplate) if err != nil { return "", err } defer f.Close() tplBytes, err := ioutil.ReadAll(f) if err != nil { return "", err } tpl = string(tplBytes) } p.config.ctx.Data = &ConfigTemplate{ CookbookPaths: strings.Join(cookbook_paths, ","), RolesPath: rolesPath, DataBagsPath: dataBagsPath, EncryptedDataBagSecretPath: encryptedDataBagSecretPath, EnvironmentsPath: environmentsPath, HasRolesPath: rolesPath != "", HasDataBagsPath: dataBagsPath != "", HasEncryptedDataBagSecretPath: encryptedDataBagSecretPath != "", HasEnvironmentsPath: environmentsPath != "", ChefEnvironment: chefEnvironment, } configString, err := interpolate.Render(tpl, &p.config.ctx) if err != nil { return "", err } remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "solo.rb")) if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil { return "", err } return remotePath, nil }
func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, remotePath string, localPath string) error { ui.Message(fmt.Sprintf("Uploading %s...", localPath)) f, err := os.Open(localPath) if err != nil { return err } defer f.Close() return comm.Upload(remotePath, f, nil) }
func (p *Provisioner) uploadDir(ui packer.Ui, comm packer.Communicator, dst, src string, ignore []string) error { if err := p.createDir(ui, comm, dst); err != nil { return err } // Make sure there is a trailing "/" so that the directory isn't // created on the other side. if src[len(src)-1] != '/' { src = src + "/" } return comm.UploadDir(dst, src, ignore) }
func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst, src string) error { f, err := os.Open(src) if err != nil { return fmt.Errorf("Error opening: %s", err) } defer f.Close() if err = comm.Upload(dst, f, nil); err != nil { return fmt.Errorf("Error uploading %s: %s", src, err) } return nil }
func uploadMinionConfig(comm packer.Communicator, dst string, src string) error { f, err := os.Open(src) if err != nil { return fmt.Errorf("Error opening minion config: %s", err) } defer f.Close() if err = comm.Upload(dst, f); err != nil { return fmt.Errorf("Error uploading minion config: %s", err) } return nil }
func (p *Provisioner) ProvisionDownload(ui packer.Ui, comm packer.Communicator) error { ui.Say(fmt.Sprintf("Downloading %s => %s", p.config.Source, p.config.Destination)) f, err := os.OpenFile(p.config.Destination, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { return err } defer f.Close() err = comm.Download(p.config.Source, f) if err != nil { ui.Error(fmt.Sprintf("Download failed: %s", err)) } return err }
func (p *Provisioner) uploadHieraConfig(ui packer.Ui, comm packer.Communicator) (string, error) { ui.Message("Uploading hiera configuration...") f, err := os.Open(p.config.HieraConfigPath) if err != nil { return "", err } defer f.Close() path := fmt.Sprintf("%s/hiera.yaml", p.config.StagingDir) if err := comm.Upload(path, f, nil); err != nil { return "", err } return path, nil }
func (p *Provisioner) copyValidationKey(ui packer.Ui, comm packer.Communicator, remotePath string) error { ui.Message("Uploading validation key...") // First upload the validation key to a writable location f, err := os.Open(p.config.ValidationKeyPath) if err != nil { return err } defer f.Close() if err := comm.Upload(remotePath, f); err != nil { return err } return nil }
func (p *Provisioner) uploadManifests(ui packer.Ui, comm packer.Communicator) (string, error) { // Create the remote manifests directory... ui.Message("Uploading manifests...") remoteManifestsPath := fmt.Sprintf("%s/manifests", p.config.StagingDir) if err := p.createDir(ui, comm, remoteManifestsPath); err != nil { return "", fmt.Errorf("Error creating manifests directory: %s", err) } // NOTE! manifest_file may either be a directory or a file, as puppet apply // now accepts either one. fi, err := os.Stat(p.config.ManifestFile) if err != nil { return "", fmt.Errorf("Error inspecting manifest file: %s", err) } if fi.IsDir() { // If manifest_file is a directory we'll upload the whole thing ui.Message(fmt.Sprintf( "Uploading manifest directory from: %s", p.config.ManifestFile)) remoteManifestDir := fmt.Sprintf("%s/manifests", p.config.StagingDir) err := p.uploadDirectory(ui, comm, remoteManifestDir, p.config.ManifestFile) if err != nil { return "", fmt.Errorf("Error uploading manifest dir: %s", err) } return remoteManifestDir, nil } else { // Otherwise manifest_file is a file and we'll upload it ui.Message(fmt.Sprintf( "Uploading manifest file from: %s", p.config.ManifestFile)) f, err := os.Open(p.config.ManifestFile) if err != nil { return "", err } defer f.Close() manifestFilename := filepath.Base(p.config.ManifestFile) remoteManifestFile := fmt.Sprintf("%s/%s", remoteManifestsPath, manifestFilename) if err := comm.Upload(remoteManifestFile, f, nil); err != nil { return "", err } return remoteManifestFile, nil } }
func CreateRemoteDirectory(path string, comm packer.Communicator) (err error) { log.Printf("Creating remote directory: %s ", path) var copyCommand = []string{"mkdir -p", path} var cmd packer.RemoteCmd cmd.Command = strings.Join(copyCommand, " ") var stdout bytes.Buffer cmd.Stdout = &stdout // Start the command if err := comm.Start(&cmd); err != nil { return fmt.Errorf("Unable to create remote directory %s: %d", path, err) } // Wait for it to complete cmd.Wait() return }
func scpDownloadSession(opts []byte, rest string, in io.Reader, out io.Writer, comm packer.Communicator) error { rest = strings.TrimSpace(rest) if len(rest) == 0 { fmt.Fprintf(out, scpEmptyError) return errors.New("no scp source specified") } d, err := ioutil.TempDir("", "packer-ansible-download") if err != nil { fmt.Fprintf(out, scpEmptyError) return err } defer os.RemoveAll(d) if bytes.Contains([]byte{'d'}, opts) { // the only ansible module that supports downloading via scp is fetch, // fetch only supports file downloads as of Ansible 2.1. fmt.Fprintf(out, scpEmptyError) return errors.New("directory downloads not supported") } f, err := os.Create(filepath.Join(d, filepath.Base(rest))) if err != nil { fmt.Fprintf(out, scpEmptyError) return err } defer f.Close() err = comm.Download(rest, f) if err != nil { fmt.Fprintf(out, scpEmptyError) return err } state := &scpDownloadState{srcRoot: d} return state.Protocol(bufio.NewReader(in), out) }
func (p *Provisioner) uploadManifests(ui packer.Ui, comm packer.Communicator) (string, error) { // Create the remote manifests directory... ui.Message("Uploading manifests...") remoteManifestsPath := fmt.Sprintf("%s/manifests", p.config.StagingDir) if err := p.createDir(ui, comm, remoteManifestsPath); err != nil { return "", fmt.Errorf("Error creating manifests directory: %s", err) } // Upload the main manifest f, err := os.Open(p.config.ManifestFile) if err != nil { return "", err } defer f.Close() manifestFilename := filepath.Base(p.config.ManifestFile) remoteManifestFile := fmt.Sprintf("%s/%s", remoteManifestsPath, manifestFilename) if err := comm.Upload(remoteManifestFile, f, nil); err != nil { return "", err } return remoteManifestFile, nil }
func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communicator, ui packer.Ui) (err error) { visitPath := func(localPath string, f os.FileInfo, err error) (err2 error) { localRelPath := strings.Replace(localPath, localDir, "", 1) localRelPath = strings.Replace(localRelPath, "\\", "/", -1) remotePath := fmt.Sprintf("%s%s", remoteDir, localRelPath) if f.IsDir() && f.Name() == ".git" { return filepath.SkipDir } if f.IsDir() { // Make remote directory cmd := &packer.RemoteCmd{Command: fmt.Sprintf("mkdir -p %s", remotePath)} if err = cmd.StartWithUi(comm, ui); err != nil { return err } } else { // Upload file to existing directory file, err := os.Open(localPath) if err != nil { return fmt.Errorf("Error opening file: %s", err) } defer file.Close() ui.Message(fmt.Sprintf("Uploading file %s: %s", localPath, remotePath)) if err = comm.Upload(remotePath, file); err != nil { return fmt.Errorf("Error uploading file: %s", err) } } return } err = filepath.Walk(localDir, visitPath) if err != nil { return fmt.Errorf("Error uploading local directory %s: %s", localDir, err) } return nil }
func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, remoteKeyPath string, validationClientName string) (string, error) { ui.Message("Creating configuration file 'client.rb'") // Read the template tpl := DefaultConfigTemplate if p.config.ConfigTemplate != "" { f, err := os.Open(p.config.ConfigTemplate) if err != nil { return "", err } defer f.Close() tplBytes, err := ioutil.ReadAll(f) if err != nil { return "", err } tpl = string(tplBytes) } configString, err := p.config.tpl.Process(tpl, &ConfigTemplate{ NodeName: nodeName, ServerUrl: serverUrl, ValidationKeyPath: remoteKeyPath, ValidationClientName: validationClientName, }) if err != nil { return "", err } remotePath := filepath.Join(p.config.StagingDir, "client.rb") if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString))); err != nil { return "", err } return remotePath, 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 }