config: `module` structures parse
This commit is contained in:
parent
2317f7ea9b
commit
2a6990e2b9
|
@ -15,6 +15,7 @@ import (
|
|||
// Config is the configuration that comes from loading a collection
|
||||
// of Terraform templates.
|
||||
type Config struct {
|
||||
Modules []*Module
|
||||
ProviderConfigs []*ProviderConfig
|
||||
Resources []*Resource
|
||||
Variables []*Variable
|
||||
|
@ -25,6 +26,17 @@ type Config struct {
|
|||
unknownKeys []string
|
||||
}
|
||||
|
||||
// Module is a module used within a configuration.
|
||||
//
|
||||
// This does not represent a module itself, this represents a module
|
||||
// call-site within an existing configuration.
|
||||
type Module struct {
|
||||
Name string
|
||||
Type string
|
||||
Source string
|
||||
RawConfig *RawConfig
|
||||
}
|
||||
|
||||
// ProviderConfig is the configuration for a resource provider.
|
||||
//
|
||||
// For example, Terraform needs to set the AWS access keys for the AWS
|
||||
|
@ -92,6 +104,11 @@ func ProviderConfigName(t string, pcs []*ProviderConfig) string {
|
|||
return lk
|
||||
}
|
||||
|
||||
// A unique identifier for this module.
|
||||
func (r *Module) Id() string {
|
||||
return fmt.Sprintf("%s.%s", r.Type, r.Name)
|
||||
}
|
||||
|
||||
// A unique identifier for this resource.
|
||||
func (r *Resource) Id() string {
|
||||
return fmt.Sprintf("%s.%s", r.Type, r.Name)
|
||||
|
|
|
@ -17,6 +17,7 @@ type hclConfigurable struct {
|
|||
|
||||
func (t *hclConfigurable) Config() (*Config, error) {
|
||||
validKeys := map[string]struct{}{
|
||||
"module": struct{}{},
|
||||
"output": struct{}{},
|
||||
"provider": struct{}{},
|
||||
"resource": struct{}{},
|
||||
|
@ -69,6 +70,15 @@ func (t *hclConfigurable) Config() (*Config, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Build the modules
|
||||
if modules := t.Object.Get("module", false); modules != nil {
|
||||
var err error
|
||||
config.Modules, err = loadModulesHcl(modules)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Build the provider configs
|
||||
if providers := t.Object.Get("provider", false); providers != nil {
|
||||
var err error
|
||||
|
@ -177,6 +187,81 @@ func loadFileHcl(root string) (configurable, []string, error) {
|
|||
return result, nil, nil
|
||||
}
|
||||
|
||||
// Given a handle to a HCL object, this recurses into the structure
|
||||
// and pulls out a list of modules.
|
||||
//
|
||||
// The resulting modules may not be unique, but each module
|
||||
// represents exactly one module definition in the HCL configuration.
|
||||
// We leave it up to another pass to merge them together.
|
||||
func loadModulesHcl(os *hclobj.Object) ([]*Module, error) {
|
||||
var allTypes []*hclobj.Object
|
||||
|
||||
// See loadResourcesHcl for why this exists. Don't touch this.
|
||||
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
|
||||
var result []*Module
|
||||
|
||||
// Now go over all the types and their children in order to get
|
||||
// all of the actual resources.
|
||||
for _, t := range allTypes {
|
||||
for _, obj := range t.Elem(true) {
|
||||
k := obj.Key
|
||||
|
||||
var config map[string]interface{}
|
||||
if err := hcl.DecodeObject(&config, obj); err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading config for %s[%s]: %s",
|
||||
t.Key,
|
||||
k,
|
||||
err)
|
||||
}
|
||||
|
||||
// Remove the fields we handle specially
|
||||
delete(config, "source")
|
||||
|
||||
rawConfig, err := NewRawConfig(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading config for %s[%s]: %s",
|
||||
t.Key,
|
||||
k,
|
||||
err)
|
||||
}
|
||||
|
||||
// If we have a count, then figure it out
|
||||
var source string
|
||||
if o := obj.Get("source", false); o != nil {
|
||||
err = hcl.DecodeObject(&source, o)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error parsing source for %s[%s]: %s",
|
||||
t.Key,
|
||||
k,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, &Module{
|
||||
Name: k,
|
||||
Type: t.Key,
|
||||
Source: source,
|
||||
RawConfig: rawConfig,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// LoadOutputsHcl recurses into the given HCL object and turns
|
||||
// it into a mapping of outputs.
|
||||
func loadOutputsHcl(os *hclobj.Object) ([]*Output, error) {
|
||||
|
|
|
@ -106,6 +106,22 @@ func TestLoadBasic_json(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadBasic_modules(t *testing.T) {
|
||||
c, err := Load(filepath.Join(fixtureDir, "modules.tf"))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if c == nil {
|
||||
t.Fatal("config should not be nil")
|
||||
}
|
||||
|
||||
actual := modulesStr(c.Modules)
|
||||
if actual != strings.TrimSpace(modulesModulesStr) {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_variables(t *testing.T) {
|
||||
c, err := Load(filepath.Join(fixtureDir, "variables.tf"))
|
||||
if err != nil {
|
||||
|
@ -302,6 +318,41 @@ func TestLoad_connections(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func modulesStr(ms []*Module) string {
|
||||
result := ""
|
||||
order := make([]int, 0, len(ms))
|
||||
ks := make([]string, 0, len(ms))
|
||||
mapping := make(map[string]int)
|
||||
for i, m := range ms {
|
||||
k := m.Id()
|
||||
ks = append(ks, k)
|
||||
mapping[k] = i
|
||||
}
|
||||
sort.Strings(ks)
|
||||
for _, k := range ks {
|
||||
order = append(order, mapping[k])
|
||||
}
|
||||
|
||||
for _, i := range order {
|
||||
m := ms[i]
|
||||
result += fmt.Sprintf("%s\n", m.Id())
|
||||
|
||||
ks := make([]string, 0, len(m.RawConfig.Raw))
|
||||
for k, _ := range m.RawConfig.Raw {
|
||||
ks = append(ks, k)
|
||||
}
|
||||
sort.Strings(ks)
|
||||
|
||||
result += fmt.Sprintf(" source = %s\n", m.Source)
|
||||
|
||||
for _, k := range ks {
|
||||
result += fmt.Sprintf(" %s\n", k)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(result)
|
||||
}
|
||||
|
||||
// This helper turns a provider configs field into a deterministic
|
||||
// string value for comparison in tests.
|
||||
func providerConfigsStr(pcs []*ProviderConfig) string {
|
||||
|
@ -611,6 +662,12 @@ foo
|
|||
bar
|
||||
`
|
||||
|
||||
const modulesModulesStr = `
|
||||
foo.bar
|
||||
source = baz
|
||||
memory
|
||||
`
|
||||
|
||||
const provisionerResourcesStr = `
|
||||
aws_instance[web] (x1)
|
||||
ami
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
module "foo" "bar" {
|
||||
memory = "1G"
|
||||
source = "baz"
|
||||
}
|
Loading…
Reference in New Issue