config: LoadDir loads override files
This commit is contained in:
parent
9d2e83d56d
commit
06cdd4fa42
|
@ -3,7 +3,6 @@ package config
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// configurable is an interface that must be implemented by any configuration
|
||||
|
@ -33,11 +32,15 @@ type fileLoaderFunc func(path string) (configurable, []string, error)
|
|||
// executes the proper fileLoaderFunc.
|
||||
func loadTree(root string) (*importTree, error) {
|
||||
var f fileLoaderFunc
|
||||
if strings.HasSuffix(root, ".tf") {
|
||||
switch ext(root) {
|
||||
case ".tf":
|
||||
fallthrough
|
||||
case ".tf.json":
|
||||
f = loadFileLibucl
|
||||
} else if strings.HasSuffix(root, ".tf.json") {
|
||||
f = loadFileLibucl
|
||||
} else {
|
||||
default:
|
||||
}
|
||||
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf(
|
||||
"%s: unknown configuration format. Use '.tf' or '.tf.json' extension",
|
||||
root)
|
||||
|
|
|
@ -2,7 +2,11 @@ package config
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Load loads the Terraform configuration from a given file.
|
||||
|
@ -30,20 +34,67 @@ func Load(path string) (*Config, error) {
|
|||
|
||||
// LoadDir loads all the Terraform configuration files in a single
|
||||
// directory and merges them together.
|
||||
func LoadDir(path string) (*Config, error) {
|
||||
matches, err := filepath.Glob(filepath.Join(path, "*.tf"))
|
||||
func LoadDir(root string) (*Config, error) {
|
||||
var files, overrides []string
|
||||
|
||||
f, err := os.Open(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(matches) == 0 {
|
||||
err = nil
|
||||
for err != io.EOF {
|
||||
var fis []os.FileInfo
|
||||
fis, err = f.Readdir(128)
|
||||
if err != nil && err != io.EOF {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, fi := range fis {
|
||||
// Ignore directories
|
||||
if fi.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Only care about files that are valid to load
|
||||
name := fi.Name()
|
||||
extValue := ext(name)
|
||||
if extValue == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Determine if we're dealing with an override
|
||||
nameNoExt := name[:len(name)-len(extValue)]
|
||||
override := nameNoExt == "override" ||
|
||||
strings.HasSuffix(nameNoExt, "_override")
|
||||
|
||||
path := filepath.Join(root, name)
|
||||
if override {
|
||||
overrides = append(overrides, path)
|
||||
} else {
|
||||
files = append(files, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the directory, we're done with it
|
||||
f.Close()
|
||||
|
||||
if len(files) == 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"No Terraform configuration files found in directory: %s",
|
||||
path)
|
||||
root)
|
||||
}
|
||||
|
||||
var result *Config
|
||||
for _, f := range matches {
|
||||
|
||||
// Sort the files and overrides so we have a deterministic order
|
||||
sort.Strings(files)
|
||||
sort.Strings(overrides)
|
||||
|
||||
// Load all the regular files, append them to each other.
|
||||
for _, f := range files {
|
||||
c, err := Load(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -59,5 +110,30 @@ func LoadDir(path string) (*Config, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Load all the overrides, and merge them into the config
|
||||
for _, f := range overrides {
|
||||
c, err := Load(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err = Merge(result, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Ext returns the Terraform configuration extension of the given
|
||||
// path, or a blank string if it is an invalid function.
|
||||
func ext(path string) string {
|
||||
if strings.HasSuffix(path, ".tf") {
|
||||
return ".tf"
|
||||
} else if strings.HasSuffix(path, ".tf.json") {
|
||||
return ".tf.json"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,6 +171,37 @@ func TestLoadDir_noMerge(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadDir_override(t *testing.T) {
|
||||
c, err := LoadDir(filepath.Join(fixtureDir, "dir-override"))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if c == nil {
|
||||
t.Fatal("config should not be nil")
|
||||
}
|
||||
|
||||
actual := variablesStr(c.Variables)
|
||||
if actual != strings.TrimSpace(dirOverrideVariablesStr) {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
|
||||
actual = providerConfigsStr(c.ProviderConfigs)
|
||||
if actual != strings.TrimSpace(dirOverrideProvidersStr) {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
|
||||
actual = resourcesStr(c.Resources)
|
||||
if actual != strings.TrimSpace(dirOverrideResourcesStr) {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
|
||||
actual = outputsStr(c.Outputs)
|
||||
if actual != strings.TrimSpace(dirOverrideOutputsStr) {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func outputsStr(os []*Output) string {
|
||||
ns := make([]string, 0, len(os))
|
||||
m := make(map[string]*Output)
|
||||
|
@ -503,6 +534,42 @@ foo
|
|||
bar
|
||||
`
|
||||
|
||||
const dirOverrideOutputsStr = `
|
||||
web_ip
|
||||
vars
|
||||
resource: aws_instance.web.private_ip
|
||||
`
|
||||
|
||||
const dirOverrideProvidersStr = `
|
||||
aws
|
||||
access_key
|
||||
secret_key
|
||||
do
|
||||
api_key
|
||||
vars
|
||||
user: var.foo
|
||||
`
|
||||
|
||||
const dirOverrideResourcesStr = `
|
||||
aws_instance[db] (x1)
|
||||
ami
|
||||
security_groups
|
||||
aws_instance[web] (x1)
|
||||
ami
|
||||
network_interface
|
||||
security_groups
|
||||
vars
|
||||
resource: aws_security_group.firewall.foo
|
||||
user: var.foo
|
||||
aws_security_group[firewall] (x5)
|
||||
`
|
||||
|
||||
const dirOverrideVariablesStr = `
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
`
|
||||
|
||||
const importProvidersStr = `
|
||||
aws
|
||||
bar
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
variable "foo" {
|
||||
default = "bar";
|
||||
description = "bar";
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
access_key = "foo";
|
||||
secret_key = "bar";
|
||||
}
|
||||
|
||||
resource "aws_instance" "db" {
|
||||
security_groups = "${aws_security_group.firewall.*.id}"
|
||||
}
|
||||
|
||||
output "web_ip" {
|
||||
value = "${aws_instance.web.private_ip}"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"resource": {
|
||||
"aws_instance": {
|
||||
"db": {
|
||||
"ami": "foo",
|
||||
"security_groups": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
provider "do" {
|
||||
api_key = "${var.foo}";
|
||||
}
|
||||
|
||||
resource "aws_security_group" "firewall" {
|
||||
count = 5
|
||||
}
|
||||
|
||||
resource aws_instance "web" {
|
||||
ami = "${var.foo}"
|
||||
security_groups = [
|
||||
"foo",
|
||||
"${aws_security_group.firewall.foo}"
|
||||
]
|
||||
|
||||
network_interface {
|
||||
device_index = 0
|
||||
description = "Main network interface"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue