github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/builtin/provisioners/local-exec/resource_provisioner.go (about) 1 package localexec 2 3 import ( 4 "fmt" 5 "io" 6 "os/exec" 7 "runtime" 8 9 "github.com/armon/circbuf" 10 "github.com/hashicorp/terraform/helper/config" 11 "github.com/hashicorp/terraform/terraform" 12 "github.com/mitchellh/go-linereader" 13 ) 14 15 const ( 16 // maxBufSize limits how much output we collect from a local 17 // invocation. This is to prevent TF memory usage from growing 18 // to an enormous amount due to a faulty process. 19 maxBufSize = 8 * 1024 20 ) 21 22 type ResourceProvisioner struct{} 23 24 func (p *ResourceProvisioner) Apply( 25 o terraform.UIOutput, 26 s *terraform.InstanceState, 27 c *terraform.ResourceConfig) error { 28 29 // Get the command 30 commandRaw, ok := c.Config["command"] 31 if !ok { 32 return fmt.Errorf("local-exec provisioner missing 'command'") 33 } 34 command, ok := commandRaw.(string) 35 if !ok { 36 return fmt.Errorf("local-exec provisioner command must be a string") 37 } 38 39 // Execute the command using a shell 40 var shell, flag string 41 if runtime.GOOS == "windows" { 42 shell = "cmd" 43 flag = "/C" 44 } else { 45 shell = "/bin/sh" 46 flag = "-c" 47 } 48 49 // Setup the reader that will read the lines from the command 50 pr, pw := io.Pipe() 51 copyDoneCh := make(chan struct{}) 52 go p.copyOutput(o, pr, copyDoneCh) 53 54 // Setup the command 55 cmd := exec.Command(shell, flag, command) 56 output, _ := circbuf.NewBuffer(maxBufSize) 57 cmd.Stderr = io.MultiWriter(output, pw) 58 cmd.Stdout = io.MultiWriter(output, pw) 59 60 // Output what we're about to run 61 o.Output(fmt.Sprintf( 62 "Executing: %s %s \"%s\"", 63 shell, flag, command)) 64 65 // Run the command to completion 66 err := cmd.Run() 67 68 // Close the write-end of the pipe so that the goroutine mirroring output 69 // ends properly. 70 pw.Close() 71 <-copyDoneCh 72 73 if err != nil { 74 return fmt.Errorf("Error running command '%s': %v. Output: %s", 75 command, err, output.Bytes()) 76 } 77 78 return nil 79 } 80 81 func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) ([]string, []error) { 82 validator := config.Validator{ 83 Required: []string{"command"}, 84 } 85 return validator.Validate(c) 86 } 87 88 func (p *ResourceProvisioner) copyOutput( 89 o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) { 90 defer close(doneCh) 91 lr := linereader.New(r) 92 for line := range lr.Ch { 93 o.Output(line) 94 } 95 }