Skip to content

Commit

Permalink
config: detect provisioner-only resource in JSON and error [GH-4385]
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Jan 19, 2016
1 parent 7ab19c6 commit fce7799
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
45 changes: 45 additions & 0 deletions config/loader_hcl.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"

"github.com/hashicorp/go-multierror"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/mitchellh/mapstructure"
Expand Down Expand Up @@ -405,6 +406,15 @@ func loadResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
// Now go over all the types and their children in order to get
// all of the actual resources.
for _, item := range list.Items {
// GH-4385: We detect a pure provisioner resource and give the user
// an error about how to do it cleanly.
if len(item.Keys) == 4 && item.Keys[2].Token.Value().(string) == "provisioner" {
return nil, fmt.Errorf(
"position %s: provisioners in a resource should be wrapped in a list\n\n"+
"Example: \"provisioner\": [ { \"local-exec\": ... } ]",
item.Pos())
}

if len(item.Keys) != 2 {
return nil, fmt.Errorf(
"position %s: resource must be followed by exactly two strings, a type and a name",
Expand Down Expand Up @@ -524,6 +534,13 @@ func loadResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
// destroying the existing instance
var lifecycle ResourceLifecycle
if o := listVal.Filter("lifecycle"); len(o.Items) > 0 {
// Check for invalid keys
valid := []string{"create_before_destroy", "ignore_changes", "prevent_destroy"}
if err := checkHCLKeys(o.Items[0].Val, valid); err != nil {
return nil, multierror.Prefix(err, fmt.Sprintf(
"%s[%s]:", t, k))
}

var raw map[string]interface{}
if err = hcl.DecodeObject(&raw, o.Items[0].Val); err != nil {
return nil, fmt.Errorf(
Expand Down Expand Up @@ -645,3 +662,31 @@ func hclObjectMap(os *hclobj.Object) map[string]ast.ListNode {
return objects
}
*/

func checkHCLKeys(node ast.Node, valid []string) error {
var list *ast.ObjectList
switch n := node.(type) {
case *ast.ObjectList:
list = n
case *ast.ObjectType:
list = n.List
default:
return fmt.Errorf("cannot check HCL keys of type %T", n)
}

validMap := make(map[string]struct{}, len(valid))
for _, v := range valid {
validMap[v] = struct{}{}
}

var result error
for _, item := range list.Items {
key := item.Keys[0].Token.Value().(string)
if _, ok := validMap[key]; !ok {
result = multierror.Append(result, fmt.Errorf(
"invalid key: %s", key))
}
}

return result
}
9 changes: 9 additions & 0 deletions config/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ func TestLoadFile_badType(t *testing.T) {
}
}

func TestLoadFile_lifecycleKeyCheck(t *testing.T) {
_, err := LoadFile(filepath.Join(fixtureDir, "lifecycle_cbd_typo.tf"))
if err == nil {
t.Fatal("should have error")
}

t.Logf("err: %s", err)
}

func TestLoadFile_resourceArityMistake(t *testing.T) {
_, err := LoadFile(filepath.Join(fixtureDir, "resource-arity-mistake.tf"))
if err == nil {
Expand Down

0 comments on commit fce7799

Please sign in to comment.