func (p *Provisioner) copyOutput(o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) { defer close(doneCh) lr := linereader.New(r) for line := range lr.Ch { o.Output(line) } }
func (p *ResourceProvisioner) Apply( o terraform.UIOutput, s *terraform.InstanceState, c *terraform.ResourceConfig) error { // Get the command commandRaw, ok := c.Config["command"] if !ok { return fmt.Errorf("local-exec provisioner missing 'command'") } command, ok := commandRaw.(string) if !ok { return fmt.Errorf("local-exec provisioner command must be a string") } // Execute the command using a shell var shell, flag string if runtime.GOOS == "windows" { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } // Setup the reader that will read the lines from the command pr, pw := io.Pipe() copyDoneCh := make(chan struct{}) go p.copyOutput(o, pr, copyDoneCh) // Setup the command cmd := exec.Command(shell, flag, command) output, _ := circbuf.NewBuffer(maxBufSize) cmd.Stderr = io.MultiWriter(output, pw) cmd.Stdout = io.MultiWriter(output, pw) // Output what we're about to run o.Output(fmt.Sprintf( "Executing: %s %s \"%s\"", shell, flag, command)) // Run the command to completion err := cmd.Run() // Close the write-end of the pipe so that the goroutine mirroring output // ends properly. pw.Close() <-copyDoneCh if err != nil { return fmt.Errorf("Error running command '%s': %v. Output: %s", command, err, output.Bytes()) } return nil }
// Connect implementation of communicator.Communicator interface func (c *Communicator) Connect(o terraform.UIOutput) error { if c.client != nil { return nil } params := winrm.DefaultParameters() params.Timeout = formatDuration(c.Timeout()) client, err := winrm.NewClientWithParameters( c.endpoint, c.connInfo.User, c.connInfo.Password, params) if err != nil { return err } if o != nil { o.Output(fmt.Sprintf( "Connecting to remote host via WinRM...\n"+ " Host: %s\n"+ " Port: %d\n"+ " User: %s\n"+ " Password: %t\n"+ " HTTPS: %t\n"+ " Insecure: %t\n"+ " CACert: %t", c.connInfo.Host, c.connInfo.Port, c.connInfo.User, c.connInfo.Password != "", c.connInfo.HTTPS, c.connInfo.Insecure, c.connInfo.CACert != nil, )) } log.Printf("connecting to remote shell using WinRM") shell, err := client.CreateShell() if err != nil { log.Printf("connection error: %s", err) return err } err = shell.Close() if err != nil { log.Printf("error closing connection: %s", err) return err } if o != nil { o.Output("Connected!") } c.client = client return nil }
// Connect implementation of communicator.Communicator interface func (c *Communicator) Connect(o terraform.UIOutput) (err error) { if c.conn != nil { c.conn.Close() } // Set the conn and client to nil since we'll recreate it c.conn = nil c.client = nil if o != nil { o.Output(fmt.Sprintf( "Connecting to remote host via SSH...\n"+ " Host: %s\n"+ " User: %s\n"+ " Password: %t\n"+ " Private key: %t\n"+ " SSH Agent: %t", c.connInfo.Host, c.connInfo.User, c.connInfo.Password != "", c.connInfo.PrivateKey != "", c.connInfo.Agent, )) if c.connInfo.BastionHost != "" { o.Output(fmt.Sprintf( "Using configured bastion host...\n"+ " Host: %s\n"+ " User: %s\n"+ " Password: %t\n"+ " Private key: %t\n"+ " SSH Agent: %t", c.connInfo.BastionHost, c.connInfo.BastionUser, c.connInfo.BastionPassword != "", c.connInfo.BastionPrivateKey != "", c.connInfo.Agent, )) } } log.Printf("connecting to TCP connection for SSH") c.conn, err = c.config.connection() if err != nil { // Explicitly set this to the REAL nil. Connection() can return // a nil implementation of net.Conn which will make the // "if c.conn == nil" check fail above. Read here for more information // on this psychotic language feature: // // http://golang.org/doc/faq#nil_error c.conn = nil log.Printf("connection error: %s", err) return err } log.Printf("handshaking with SSH") host := fmt.Sprintf("%s:%d", c.connInfo.Host, c.connInfo.Port) sshConn, sshChan, req, err := ssh.NewClientConn(c.conn, host, c.config.config) if err != nil { log.Printf("handshake error: %s", err) return err } c.client = ssh.NewClient(sshConn, sshChan, req) if c.config.sshAgent != nil { log.Printf("[DEBUG] Telling SSH config to forward to agent") if err := c.config.sshAgent.ForwardToAgent(c.client); err != nil { return err } log.Printf("[DEBUG] Setting up a session to request agent forwarding") session, err := c.newSession() if err != nil { return err } defer session.Close() err = agent.RequestAgentForwarding(session) if err == nil { log.Printf("[INFO] agent forwarding enabled") } else { log.Printf("[WARN] error forwarding agent: %s", err) } } if o != nil { o.Output("Connected!") } return err }
// Apply executes the file provisioner func (r *ResourceProvisioner) Apply( o terraform.UIOutput, s *terraform.InstanceState, c *terraform.ResourceConfig) error { // Decode the raw config for this provisioner p, err := r.decodeConfig(c) if err != nil { return err } if p.OSType == "" { switch s.Ephemeral.ConnInfo["type"] { case "ssh", "": // The default connection type is ssh, so if the type is empty assume ssh p.OSType = "linux" case "winrm": p.OSType = "windows" default: return fmt.Errorf("Unsupported connection type: %s", s.Ephemeral.ConnInfo["type"]) } } // Set some values based on the targeted OS switch p.OSType { case "linux": p.installChefClient = p.linuxInstallChefClient p.createConfigFiles = p.linuxCreateConfigFiles p.runChefClient = p.runChefClientFunc(linuxChefCmd, linuxConfDir) p.useSudo = !p.PreventSudo && s.Ephemeral.ConnInfo["user"] != "root" case "windows": p.installChefClient = p.windowsInstallChefClient p.createConfigFiles = p.windowsCreateConfigFiles p.runChefClient = p.runChefClientFunc(windowsChefCmd, windowsConfDir) p.useSudo = false default: return fmt.Errorf("Unsupported os type: %s", p.OSType) } // Get a new communicator comm, err := communicator.New(s) if err != nil { return err } // Wait and retry until we establish the connection err = retryFunc(comm.Timeout(), func() error { err := comm.Connect(o) return err }) if err != nil { return err } defer comm.Disconnect() if !p.SkipInstall { if err := p.installChefClient(o, comm); err != nil { return err } } o.Output("Creating configuration files...") if err := p.createConfigFiles(o, comm); err != nil { return err } o.Output("Starting initial Chef-Client run...") if err := p.runChefClient(o, comm); err != nil { return err } return nil }