config: parse "when" and "on_failure" on provisioners

This commit is contained in:
Mitchell Hashimoto 2017-01-19 18:10:21 -08:00
parent 570af34f0f
commit 928fce71f7
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
8 changed files with 164 additions and 3 deletions

View File

@ -136,6 +136,9 @@ type Provisioner struct {
Type string
RawConfig *RawConfig
ConnInfo *RawConfig
When ProvisionerWhen
OnFailure ProvisionerOnFailure
}
// Copy returns a copy of this Provisioner
@ -144,6 +147,8 @@ func (p *Provisioner) Copy() *Provisioner {
Type: p.Type,
RawConfig: p.RawConfig.Copy(),
ConnInfo: p.ConnInfo.Copy(),
When: p.When,
OnFailure: p.OnFailure,
}
}
@ -553,7 +558,7 @@ func (c *Config) Validate() error {
// Validate DependsOn
errs = append(errs, c.validateDependsOn(n, r.DependsOn, resources, modules)...)
// Verify provisioners don't contain any splats
// Verify provisioners
for _, p := range r.Provisioners {
// This validation checks that there are now splat variables
// referencing ourself. This currently is not allowed.
@ -585,6 +590,17 @@ func (c *Config) Validate() error {
break
}
}
// Check for invalid when/onFailure values, though this should be
// picked up by the loader we check here just in case.
if p.When == ProvisionerWhenInvalid {
errs = append(errs, fmt.Errorf(
"%s: provisioner 'when' value is invalid", n))
}
if p.OnFailure == ProvisionerOnFailureInvalid {
errs = append(errs, fmt.Errorf(
"%s: provisioner 'on_failure' value is invalid", n))
}
}
// Verify ignore_changes contains valid entries

View File

@ -214,7 +214,16 @@ func resourcesStr(rs []*Resource) string {
if len(r.Provisioners) > 0 {
result += fmt.Sprintf(" provisioners\n")
for _, p := range r.Provisioners {
result += fmt.Sprintf(" %s\n", p.Type)
when := ""
if p.When != ProvisionerWhenCreate {
when = fmt.Sprintf(" (%s)", p.When.String())
}
result += fmt.Sprintf(" %s%s\n", p.Type, when)
if p.OnFailure != ProvisionerOnFailureFail {
result += fmt.Sprintf(" on_failure = %s\n", p.OnFailure.String())
}
ks := make([]string, 0, len(p.RawConfig.Raw))
for k, _ := range p.RawConfig.Raw {

View File

@ -168,6 +168,13 @@ func TestConfigValidate_table(t *testing.T) {
true,
"data sources cannot have",
},
{
"basic provisioners",
"validate-basic-provisioners",
false,
"",
},
}
for i, tc := range cases {

View File

@ -849,8 +849,40 @@ func loadProvisionersHcl(list *ast.ObjectList, connInfo map[string]interface{})
return nil, err
}
// Delete the "connection" section, handle separately
// Parse the "when" value
when := ProvisionerWhenCreate
if v, ok := config["when"]; ok {
switch v {
case "create":
when = ProvisionerWhenCreate
case "destroy":
when = ProvisionerWhenDestroy
default:
return nil, fmt.Errorf(
"position %s: 'provisioner' when must be 'create' or 'destroy'",
item.Pos())
}
}
// Parse the "on_failure" value
onFailure := ProvisionerOnFailureFail
if v, ok := config["on_failure"]; ok {
switch v {
case "continue":
onFailure = ProvisionerOnFailureContinue
case "fail":
onFailure = ProvisionerOnFailureFail
default:
return nil, fmt.Errorf(
"position %s: 'provisioner' on_failure must be 'continue' or 'fail'",
item.Pos())
}
}
// Delete fields we special case
delete(config, "connection")
delete(config, "when")
delete(config, "on_failure")
rawConfig, err := NewRawConfig(config)
if err != nil {
@ -889,6 +921,8 @@ func loadProvisionersHcl(list *ast.ObjectList, connInfo map[string]interface{})
Type: n,
RawConfig: rawConfig,
ConnInfo: connRaw,
When: when,
OnFailure: onFailure,
})
}

View File

@ -629,6 +629,22 @@ func TestLoadFile_provisioners(t *testing.T) {
}
}
func TestLoadFile_provisionersDestroy(t *testing.T) {
c, err := LoadFile(filepath.Join(fixtureDir, "provisioners-destroy.tf"))
if err != nil {
t.Fatalf("err: %s", err)
}
if c == nil {
t.Fatal("config should not be nil")
}
actual := resourcesStr(c.Resources)
if actual != strings.TrimSpace(provisionerDestroyResourcesStr) {
t.Fatalf("bad:\n%s", actual)
}
}
func TestLoadFile_unnamedOutput(t *testing.T) {
_, err := LoadFile(filepath.Join(fixtureDir, "output-unnamed.tf"))
if err == nil {
@ -1126,6 +1142,17 @@ aws_instance.web (x1)
user: var.foo
`
const provisionerDestroyResourcesStr = `
aws_instance.web (x1)
provisioners
shell
shell (destroy)
path
shell (destroy)
on_failure = continue
path
`
const connectionResourcesStr = `
aws_instance.web (x1)
ami

View File

@ -0,0 +1,40 @@
package config
// ProvisionerWhen is an enum for valid values for when to run provisioners.
type ProvisionerWhen int
const (
ProvisionerWhenInvalid ProvisionerWhen = iota
ProvisionerWhenCreate
ProvisionerWhenDestroy
)
var provisionerWhenStrs = map[ProvisionerWhen]string{
ProvisionerWhenInvalid: "invalid",
ProvisionerWhenCreate: "create",
ProvisionerWhenDestroy: "destroy",
}
func (v ProvisionerWhen) String() string {
return provisionerWhenStrs[v]
}
// ProvisionerOnFailure is an enum for valid values for on_failure options
// for provisioners.
type ProvisionerOnFailure int
const (
ProvisionerOnFailureInvalid ProvisionerOnFailure = iota
ProvisionerOnFailureContinue
ProvisionerOnFailureFail
)
var provisionerOnFailureStrs = map[ProvisionerOnFailure]string{
ProvisionerOnFailureInvalid: "invalid",
ProvisionerOnFailureContinue: "continue",
ProvisionerOnFailureFail: "fail",
}
func (v ProvisionerOnFailure) String() string {
return provisionerOnFailureStrs[v]
}

View File

@ -0,0 +1,14 @@
resource "aws_instance" "web" {
provisioner "shell" {}
provisioner "shell" {
path = "foo"
when = "destroy"
}
provisioner "shell" {
path = "foo"
when = "destroy"
on_failure = "continue"
}
}

View File

@ -0,0 +1,14 @@
resource "aws_instance" "web" {
provisioner "shell" {}
provisioner "shell" {
path = "foo"
when = "destroy"
}
provisioner "shell" {
path = "foo"
when = "destroy"
on_failure = "continue"
}
}