provisioner/remote-exec: fail on first inline script with bad exit code (#11155)

The provisioner collected all inline commands into a single script which meant
only the exit code of the last command was actually checked for an error.
This commit is contained in:
Peter McAtominey 2017-01-20 14:04:43 +00:00 committed by Paul Stack
parent 2f520c0e64
commit d2047d714e
2 changed files with 35 additions and 26 deletions

View File

@ -7,7 +7,6 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"strings"
"time" "time"
"github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator"
@ -63,32 +62,30 @@ func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string
return return
} }
// generateScript takes the configuration and creates a script to be executed // generateScripts takes the configuration and creates a script from each inline config
// from the inline configs func (p *ResourceProvisioner) generateScripts(c *terraform.ResourceConfig) ([]string, error) {
func (p *ResourceProvisioner) generateScript(c *terraform.ResourceConfig) (string, error) { var scripts []string
var lines []string
command, ok := c.Config["inline"] command, ok := c.Config["inline"]
if ok { if ok {
switch cmd := command.(type) { switch cmd := command.(type) {
case string: case string:
lines = append(lines, cmd) scripts = append(scripts, cmd)
case []string: case []string:
lines = append(lines, cmd...) scripts = append(scripts, cmd...)
case []interface{}: case []interface{}:
for _, l := range cmd { for _, l := range cmd {
lStr, ok := l.(string) lStr, ok := l.(string)
if ok { if ok {
lines = append(lines, lStr) scripts = append(scripts, lStr)
} else { } else {
return "", fmt.Errorf("Unsupported 'inline' type! Must be string, or list of strings.") return nil, fmt.Errorf("Unsupported 'inline' type! Must be string, or list of strings.")
} }
} }
default: default:
return "", fmt.Errorf("Unsupported 'inline' type! Must be string, or list of strings.") return nil, fmt.Errorf("Unsupported 'inline' type! Must be string, or list of strings.")
} }
} }
lines = append(lines, "") return scripts, nil
return strings.Join(lines, "\n"), nil
} }
// collectScripts is used to collect all the scripts we need // collectScripts is used to collect all the scripts we need
@ -97,12 +94,17 @@ func (p *ResourceProvisioner) collectScripts(c *terraform.ResourceConfig) ([]io.
// Check if inline // Check if inline
_, ok := c.Config["inline"] _, ok := c.Config["inline"]
if ok { if ok {
script, err := p.generateScript(c) scripts, err := p.generateScripts(c)
if err != nil { if err != nil {
return nil, err return nil, err
} }
rc := ioutil.NopCloser(bytes.NewReader([]byte(script)))
return []io.ReadCloser{rc}, nil r := []io.ReadCloser{}
for _, script := range scripts {
r = append(r, ioutil.NopCloser(bytes.NewReader([]byte(script))))
}
return r, nil
} }
// Collect scripts // Collect scripts

View File

@ -3,8 +3,11 @@ package remoteexec
import ( import (
"bytes" "bytes"
"io" "io"
"strings"
"testing" "testing"
"reflect"
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
@ -46,7 +49,9 @@ wget http://foobar
exit 0 exit 0
` `
func TestResourceProvider_generateScript(t *testing.T) { var expectedInlineScriptsOut = strings.Split(expectedScriptOut, "\n")
func TestResourceProvider_generateScripts(t *testing.T) {
p := new(ResourceProvisioner) p := new(ResourceProvisioner)
conf := testConfig(t, map[string]interface{}{ conf := testConfig(t, map[string]interface{}{
"inline": []interface{}{ "inline": []interface{}{
@ -55,12 +60,12 @@ func TestResourceProvider_generateScript(t *testing.T) {
"exit 0", "exit 0",
}, },
}) })
out, err := p.generateScript(conf) out, err := p.generateScripts(conf)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if out != expectedScriptOut { if reflect.DeepEqual(out, expectedInlineScriptsOut) {
t.Fatalf("bad: %v", out) t.Fatalf("bad: %v", out)
} }
} }
@ -80,18 +85,20 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if len(scripts) != 1 { if len(scripts) != 3 {
t.Fatalf("bad: %v", scripts) t.Fatalf("bad: %v", scripts)
} }
var out bytes.Buffer for i, script := range scripts {
_, err = io.Copy(&out, scripts[0]) var out bytes.Buffer
if err != nil { _, err = io.Copy(&out, script)
t.Fatalf("err: %v", err) if err != nil {
} t.Fatalf("err: %v", err)
}
if out.String() != expectedScriptOut { if out.String() != expectedInlineScriptsOut[i] {
t.Fatalf("bad: %v", out.String()) t.Fatalf("bad: %v", out.String())
}
} }
} }