config: support for provider configs
This commit is contained in:
parent
87b3046d2a
commit
ab507814b7
|
@ -12,8 +12,18 @@ import (
|
|||
// Config is the configuration that comes from loading a collection
|
||||
// of Terraform templates.
|
||||
type Config struct {
|
||||
Variables map[string]Variable
|
||||
Resources []*Resource
|
||||
ProviderConfigs map[string]*ProviderConfig
|
||||
Resources []*Resource
|
||||
Variables map[string]Variable
|
||||
}
|
||||
|
||||
// ProviderConfig is the configuration for a resource provider.
|
||||
//
|
||||
// For example, Terraform needs to set the AWS access keys for the AWS
|
||||
// resource provider.
|
||||
type ProviderConfig struct {
|
||||
Config map[string]interface{}
|
||||
Variables map[string]InterpolatedVariable
|
||||
}
|
||||
|
||||
// A resource represents a single Terraform resource in the configuration.
|
||||
|
|
|
@ -66,6 +66,16 @@ func mergeConfig(c1, c2 *Config) (*Config, error) {
|
|||
c.Variables[k] = v2
|
||||
}
|
||||
|
||||
// Merge provider configs: If they collide, we just take the latest one
|
||||
// for now. In the future, we might provide smarter merge functionality.
|
||||
c.ProviderConfigs = make(map[string]*ProviderConfig)
|
||||
for k, v := range c1.ProviderConfigs {
|
||||
c.ProviderConfigs[k] = v
|
||||
}
|
||||
for k, v := range c2.ProviderConfigs {
|
||||
c.ProviderConfigs[k] = v
|
||||
}
|
||||
|
||||
// Merge resources: If they collide, we just take the latest one
|
||||
// for now. In the future, we might provide smarter merge functionality.
|
||||
resources := make(map[string]*Resource)
|
||||
|
|
|
@ -36,6 +36,17 @@ func (t *libuclConfigurable) Config() (*Config, error) {
|
|||
config := new(Config)
|
||||
config.Variables = rawConfig.Variable
|
||||
|
||||
// Build the provider configs
|
||||
providers := t.Object.Get("provider")
|
||||
if providers != nil {
|
||||
var err error
|
||||
config.ProviderConfigs, err = loadProvidersLibucl(providers)
|
||||
providers.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Build the resources
|
||||
resources := t.Object.Get("resource")
|
||||
if resources != nil {
|
||||
|
@ -114,6 +125,52 @@ func loadFileLibucl(root string) (configurable, []string, error) {
|
|||
return result, importPaths, nil
|
||||
}
|
||||
|
||||
// LoadProvidersLibucl recurses into the given libucl object and turns
|
||||
// it into a mapping of provider configs.
|
||||
func loadProvidersLibucl(o *libucl.Object) (map[string]*ProviderConfig, error) {
|
||||
objects := make(map[string]*libucl.Object)
|
||||
|
||||
// Iterate over all the "provider" blocks and get the keys along with
|
||||
// their raw configuration objects. We'll parse those later.
|
||||
iter := o.Iterate(false)
|
||||
for o1 := iter.Next(); o1 != nil; o1 = iter.Next() {
|
||||
iter2 := o1.Iterate(true)
|
||||
for o2 := iter2.Next(); o2 != nil; o2 = iter2.Next() {
|
||||
objects[o2.Key()] = o2
|
||||
defer o2.Close()
|
||||
}
|
||||
|
||||
o1.Close()
|
||||
iter2.Close()
|
||||
}
|
||||
iter.Close()
|
||||
|
||||
// Go through each object and turn it into an actual result.
|
||||
result := make(map[string]*ProviderConfig)
|
||||
for n, o := range objects {
|
||||
var config map[string]interface{}
|
||||
|
||||
if err := o.Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
walker := new(variableDetectWalker)
|
||||
if err := reflectwalk.Walk(config, walker); err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading config for provider config %s: %s",
|
||||
n,
|
||||
err)
|
||||
}
|
||||
|
||||
result[n] = &ProviderConfig{
|
||||
Config: config,
|
||||
Variables: walker.Variables,
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Given a handle to a libucl object, this recurses into the structure
|
||||
// and pulls out a list of resources.
|
||||
//
|
||||
|
|
|
@ -29,6 +29,11 @@ func TestLoadBasic(t *testing.T) {
|
|||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
|
||||
actual = providerConfigsStr(c.ProviderConfigs)
|
||||
if actual != strings.TrimSpace(basicProvidersStr) {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
|
||||
actual = resourcesStr(c.Resources)
|
||||
if actual != strings.TrimSpace(basicResourcesStr) {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
|
@ -50,12 +55,49 @@ func TestLoadBasic_import(t *testing.T) {
|
|||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
|
||||
actual = providerConfigsStr(c.ProviderConfigs)
|
||||
if actual != strings.TrimSpace(importProvidersStr) {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
|
||||
actual = resourcesStr(c.Resources)
|
||||
if actual != strings.TrimSpace(importResourcesStr) {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// This helper turns a provider configs field into a deterministic
|
||||
// string value for comparison in tests.
|
||||
func providerConfigsStr(pcs map[string]*ProviderConfig) string {
|
||||
result := ""
|
||||
for n, pc := range pcs {
|
||||
result += fmt.Sprintf("%s\n", n)
|
||||
|
||||
for k, _ := range pc.Config {
|
||||
result += fmt.Sprintf(" %s\n", k)
|
||||
}
|
||||
|
||||
if len(pc.Variables) > 0 {
|
||||
result += fmt.Sprintf(" vars\n")
|
||||
for _, rawV := range pc.Variables {
|
||||
kind := "unknown"
|
||||
str := rawV.FullKey()
|
||||
|
||||
switch rawV.(type) {
|
||||
case *ResourceVariable:
|
||||
kind = "resource"
|
||||
case *UserVariable:
|
||||
kind = "user"
|
||||
}
|
||||
|
||||
result += fmt.Sprintf(" %s: %s\n", kind, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(result)
|
||||
}
|
||||
|
||||
// This helper turns a resources field into a deterministic
|
||||
// string value for comparison in tests.
|
||||
func resourcesStr(rs []*Resource) string {
|
||||
|
@ -113,6 +155,16 @@ func variablesStr(vs map[string]Variable) string {
|
|||
return strings.TrimSpace(result)
|
||||
}
|
||||
|
||||
const basicProvidersStr = `
|
||||
aws
|
||||
access_key
|
||||
secret_key
|
||||
do
|
||||
api_key
|
||||
vars
|
||||
user: var.foo
|
||||
`
|
||||
|
||||
const basicResourcesStr = `
|
||||
aws_security_group[firewall]
|
||||
aws_instance[web]
|
||||
|
@ -129,6 +181,11 @@ foo
|
|||
bar
|
||||
`
|
||||
|
||||
const importProvidersStr = `
|
||||
aws
|
||||
foo
|
||||
`
|
||||
|
||||
const importResourcesStr = `
|
||||
aws_security_group[db]
|
||||
aws_security_group[web]
|
||||
|
|
|
@ -3,6 +3,15 @@ variable "foo" {
|
|||
description = "bar";
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
access_key = "foo";
|
||||
secret_key = "bar";
|
||||
}
|
||||
|
||||
provider "do" {
|
||||
api_key = "${var.foo}";
|
||||
}
|
||||
|
||||
resource "aws_security_group" "firewall" {
|
||||
}
|
||||
|
||||
|
|
|
@ -5,4 +5,8 @@ variable "foo" {
|
|||
description = "bar";
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
foo = "bar";
|
||||
}
|
||||
|
||||
resource "aws_security_group" "web" {}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
variable "bar" {}
|
||||
|
||||
provider "aws" {
|
||||
bar = "baz";
|
||||
}
|
||||
|
||||
resource "aws_security_group" "db" {}
|
||||
|
|
Loading…
Reference in New Issue