forked from hashicorp/terraform
-
Notifications
You must be signed in to change notification settings - Fork 0
/
resource_provisioner.go
95 lines (79 loc) · 2.14 KB
/
resource_provisioner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package localexec
import (
"fmt"
"io"
"os/exec"
"runtime"
"github.com/armon/circbuf"
"github.com/hashicorp/terraform/helper/config"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/go-linereader"
)
const (
// maxBufSize limits how much output we collect from a local
// invocation. This is to prevent TF memory usage from growing
// to an enormous amount due to a faulty process.
maxBufSize = 8 * 1024
)
type ResourceProvisioner struct{}
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
}
func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) ([]string, []error) {
validator := config.Validator{
Required: []string{"command"},
}
return validator.Validate(c)
}
func (p *ResourceProvisioner) 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)
}
}