github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/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  }