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:
parent
2f520c0e64
commit
d2047d714e
|
@ -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
|
||||||
|
|
|
@ -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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue