config: use new HCL lib and structure

This commit is contained in:
Mitchell Hashimoto 2014-08-11 21:51:24 -07:00
parent ca5be43519
commit 2fd545bca1
1 changed files with 152 additions and 148 deletions

View File

@ -5,14 +5,14 @@ import (
"io/ioutil" "io/ioutil"
"github.com/hashicorp/hcl" "github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/ast" hclobj "github.com/hashicorp/hcl/hcl"
) )
// hclConfigurable is an implementation of configurable that knows // hclConfigurable is an implementation of configurable that knows
// how to turn HCL configuration into a *Config object. // how to turn HCL configuration into a *Config object.
type hclConfigurable struct { type hclConfigurable struct {
File string File string
Object *ast.ObjectNode Object *hclobj.Object
} }
func (t *hclConfigurable) Config() (*Config, error) { func (t *hclConfigurable) Config() (*Config, error) {
@ -33,7 +33,7 @@ func (t *hclConfigurable) Config() (*Config, error) {
Variable map[string]*hclVariable Variable map[string]*hclVariable
} }
if err := hcl.DecodeAST(&rawConfig, t.Object); err != nil { if err := hcl.DecodeObject(&rawConfig, t.Object); err != nil {
return nil, err return nil, err
} }
@ -97,8 +97,8 @@ func (t *hclConfigurable) Config() (*Config, error) {
} }
// Check for invalid keys // Check for invalid keys
for _, elem := range t.Object.Elem { for _, elem := range t.Object.Elem(true) {
k := elem.Key() k := elem.Key
if _, ok := validKeys[k]; ok { if _, ok := validKeys[k]; ok {
continue continue
} }
@ -112,7 +112,7 @@ func (t *hclConfigurable) Config() (*Config, error) {
// loadFileHcl is a fileLoaderFunc that knows how to read HCL // loadFileHcl is a fileLoaderFunc that knows how to read HCL
// files and turn them into hclConfigurables. // files and turn them into hclConfigurables.
func loadFileHcl(root string) (configurable, []string, error) { func loadFileHcl(root string) (configurable, []string, error) {
var obj *ast.ObjectNode = nil var obj *hclobj.Object = nil
// Read the HCL file and prepare for parsing // Read the HCL file and prepare for parsing
d, err := ioutil.ReadFile(root) d, err := ioutil.ReadFile(root)
@ -179,8 +179,17 @@ func loadFileHcl(root string) (configurable, []string, error) {
// LoadOutputsHcl recurses into the given HCL object and turns // LoadOutputsHcl recurses into the given HCL object and turns
// it into a mapping of outputs. // it into a mapping of outputs.
func loadOutputsHcl(ns []ast.Node) ([]*Output, error) { func loadOutputsHcl(os *hclobj.Object) ([]*Output, error) {
objects := hclObjectMap(ns) objects := make(map[string]*hclobj.Object)
// Iterate over all the "output" blocks and get the keys along with
// their raw configuration objects. We'll parse those later.
for _, o1 := range os.Elem(false) {
for _, o2 := range o1.Elem(true) {
objects[o2.Key] = o2
}
}
if len(objects) == 0 { if len(objects) == 0 {
return nil, nil return nil, nil
} }
@ -190,7 +199,7 @@ func loadOutputsHcl(ns []ast.Node) ([]*Output, error) {
for n, o := range objects { for n, o := range objects {
var config map[string]interface{} var config map[string]interface{}
if err := hcl.DecodeAST(&config, o); err != nil { if err := hcl.DecodeObject(&config, o); err != nil {
return nil, err return nil, err
} }
@ -213,8 +222,17 @@ func loadOutputsHcl(ns []ast.Node) ([]*Output, error) {
// LoadProvidersHcl recurses into the given HCL object and turns // LoadProvidersHcl recurses into the given HCL object and turns
// it into a mapping of provider configs. // it into a mapping of provider configs.
func loadProvidersHcl(ns []ast.Node) ([]*ProviderConfig, error) { func loadProvidersHcl(os *hclobj.Object) ([]*ProviderConfig, error) {
objects := hclObjectMap(ns) objects := make(map[string]*hclobj.Object)
// Iterate over all the "provider" blocks and get the keys along with
// their raw configuration objects. We'll parse those later.
for _, o1 := range os.Elem(false) {
for _, o2 := range o1.Elem(true) {
objects[o2.Key] = o2
}
}
if len(objects) == 0 { if len(objects) == 0 {
return nil, nil return nil, nil
} }
@ -224,7 +242,7 @@ func loadProvidersHcl(ns []ast.Node) ([]*ProviderConfig, error) {
for n, o := range objects { for n, o := range objects {
var config map[string]interface{} var config map[string]interface{}
if err := hcl.DecodeAST(&config, o); err != nil { if err := hcl.DecodeObject(&config, o); err != nil {
return nil, err return nil, err
} }
@ -251,25 +269,40 @@ func loadProvidersHcl(ns []ast.Node) ([]*ProviderConfig, error) {
// The resulting resources may not be unique, but each resource // The resulting resources may not be unique, but each resource
// represents exactly one resource definition in the HCL configuration. // represents exactly one resource definition in the HCL configuration.
// We leave it up to another pass to merge them together. // We leave it up to another pass to merge them together.
func loadResourcesHcl(ns []ast.Node) ([]*Resource, error) { func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) {
typeMap := hclObjectMap(ns) var allTypes []*hclobj.Object
// HCL object iteration is really nasty. Below is likely to make
// no sense to anyone approaching this code. Luckily, it is very heavily
// tested. If working on a bug fix or feature, we recommend writing a
// test first then doing whatever you want to the code below. If you
// break it, the tests will catch it. Likewise, if you change this,
// MAKE SURE you write a test for your change, because its fairly impossible
// to reason about this mess.
//
// Functionally, what the code does below is get the libucl.Objects
// for all the TYPES, such as "aws_security_group".
for _, o1 := range os.Elem(false) {
// Iterate the inner to get the list of types
for _, o2 := range o1.Elem(true) {
// Iterate all of this type to get _all_ the types
for _, o3 := range o2.Elem(false) {
allTypes = append(allTypes, o3)
}
}
}
// Where all the results will go // Where all the results will go
var result []*Resource var result []*Resource
// Now go over all the types and their children in order to get // Now go over all the types and their children in order to get
// all of the actual resources. // all of the actual resources.
for t, rs := range typeMap { for _, t := range allTypes {
resourceMap := hclObjectMap([]ast.Node{rs}) for _, obj := range t.Elem(true) {
for k, o := range resourceMap { k := obj.Key
for _, o := range o.Elem {
obj, ok := o.(ast.ObjectNode)
if !ok {
continue
}
var config map[string]interface{} var config map[string]interface{}
if err := hcl.DecodeAST(&config, o); err != nil { if err := hcl.DecodeObject(&config, obj); err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"Error reading config for %s[%s]: %s", "Error reading config for %s[%s]: %s",
t, t,
@ -294,9 +327,8 @@ func loadResourcesHcl(ns []ast.Node) ([]*Resource, error) {
// If we have a count, then figure it out // If we have a count, then figure it out
var count int = 1 var count int = 1
if os := obj.Get("count", false); os != nil { if o := obj.Get("count", false); o != nil {
for _, o := range os { err = hcl.DecodeObject(&count, o)
err = hcl.DecodeAST(&count, o)
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"Error parsing count for %s[%s]: %s", "Error parsing count for %s[%s]: %s",
@ -305,13 +337,11 @@ func loadResourcesHcl(ns []ast.Node) ([]*Resource, error) {
err) err)
} }
} }
}
// If we have depends fields, then add those in // If we have depends fields, then add those in
var dependsOn []string var dependsOn []string
if os := obj.Get("depends_on", false); os != nil { if o := obj.Get("depends_on", false); o != nil {
for _, o := range os { err := hcl.DecodeObject(&dependsOn, o)
err := hcl.DecodeAST(&dependsOn, o)
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"Error reading depends_on for %s[%s]: %s", "Error reading depends_on for %s[%s]: %s",
@ -320,13 +350,11 @@ func loadResourcesHcl(ns []ast.Node) ([]*Resource, error) {
err) err)
} }
} }
}
// If we have connection info, then parse those out // If we have connection info, then parse those out
var connInfo map[string]interface{} var connInfo map[string]interface{}
if os := obj.Get("connection", false); os != nil { if o := obj.Get("connection", false); o != nil {
for _, o := range os { err := hcl.DecodeObject(&connInfo, o)
err := hcl.DecodeAST(&connInfo, o)
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"Error reading connection info for %s[%s]: %s", "Error reading connection info for %s[%s]: %s",
@ -335,7 +363,6 @@ func loadResourcesHcl(ns []ast.Node) ([]*Resource, error) {
err) err)
} }
} }
}
// If we have provisioners, then parse those out // If we have provisioners, then parse those out
var provisioners []*Provisioner var provisioners []*Provisioner
@ -353,7 +380,7 @@ func loadResourcesHcl(ns []ast.Node) ([]*Resource, error) {
result = append(result, &Resource{ result = append(result, &Resource{
Name: k, Name: k,
Type: t, Type: t.Key,
Count: count, Count: count,
RawConfig: rawConfig, RawConfig: rawConfig,
Provisioners: provisioners, Provisioners: provisioners,
@ -361,13 +388,12 @@ func loadResourcesHcl(ns []ast.Node) ([]*Resource, error) {
}) })
} }
} }
}
return result, nil return result, nil
} }
func loadProvisionersHcl(ns []ast.Node, connInfo map[string]interface{}) ([]*Provisioner, error) { func loadProvisionersHcl(os *hclobj.Object, connInfo map[string]interface{}) ([]*Provisioner, error) {
pos := make([]ast.AssignmentNode, 0, len(ns)) pos := make([]*hclobj.Object, 0, int(os.Len()))
// Accumulate all the actual provisioner configuration objects. We // Accumulate all the actual provisioner configuration objects. We
// have to iterate twice here: // have to iterate twice here:
@ -387,14 +413,9 @@ func loadProvisionersHcl(ns []ast.Node, connInfo map[string]interface{}) ([]*Pro
// } // }
// ] // ]
// //
for _, n := range ns { for _, o1 := range os.Elem(false) {
obj, ok := n.(ast.ObjectNode) for _, o2 := range o1.Elem(true) {
if !ok { pos = append(pos, o2)
continue
}
for _, elem := range obj.Elem {
pos = append(pos, elem)
} }
} }
@ -405,13 +426,8 @@ func loadProvisionersHcl(ns []ast.Node, connInfo map[string]interface{}) ([]*Pro
result := make([]*Provisioner, 0, len(pos)) result := make([]*Provisioner, 0, len(pos))
for _, po := range pos { for _, po := range pos {
obj, ok := po.Value.(ast.ObjectNode)
if !ok {
continue
}
var config map[string]interface{} var config map[string]interface{}
if err := hcl.DecodeAST(&config, obj); err != nil { if err := hcl.DecodeObject(&config, po); err != nil {
return nil, err return nil, err
} }
@ -426,14 +442,12 @@ func loadProvisionersHcl(ns []ast.Node, connInfo map[string]interface{}) ([]*Pro
// Check if we have a provisioner-level connection // Check if we have a provisioner-level connection
// block that overrides the resource-level // block that overrides the resource-level
var subConnInfo map[string]interface{} var subConnInfo map[string]interface{}
if os := obj.Get("connection", false); os != nil { if o := po.Get("connection", false); o != nil {
for _, o := range os { err := hcl.DecodeObject(&subConnInfo, o)
err := hcl.DecodeAST(&subConnInfo, o)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
}
// Inherit from the resource connInfo any keys // Inherit from the resource connInfo any keys
// that are not explicitly overriden. // that are not explicitly overriden.
@ -454,7 +468,7 @@ func loadProvisionersHcl(ns []ast.Node, connInfo map[string]interface{}) ([]*Pro
} }
result = append(result, &Provisioner{ result = append(result, &Provisioner{
Type: po.Key(), Type: po.Key,
RawConfig: rawConfig, RawConfig: rawConfig,
ConnInfo: connRaw, ConnInfo: connRaw,
}) })
@ -463,32 +477,22 @@ func loadProvisionersHcl(ns []ast.Node, connInfo map[string]interface{}) ([]*Pro
return result, nil return result, nil
} }
func hclObjectMap(ns []ast.Node) map[string]ast.ListNode { /*
objects := make(map[string]ast.ListNode) func hclObjectMap(os *hclobj.Object) map[string]ast.ListNode {
objects := make(map[string][]*hclobj.Object)
for _, n := range ns { for _, o := range os.Elem(false) {
ns := []ast.Node{n} for _, elem := range o.Elem(true) {
if ln, ok := n.(ast.ListNode); ok { val, ok := objects[elem.Key]
ns = ln.Elem
}
for _, n := range ns {
obj, ok := n.(ast.ObjectNode)
if !ok { if !ok {
continue val = make([]*hclobj.Object, 0, 1)
} }
for _, elem := range obj.Elem { val = append(val, elem)
val, ok := objects[elem.Key()] objects[elem.Key] = val
if !ok {
val = ast.ListNode{}
}
val.Elem = append(val.Elem, elem.Value)
objects[elem.Key()] = val
}
} }
} }
return objects return objects
} }
*/