mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 01:41:48 -06:00
configs: finish deprecation of the config package by removing the remaining used functions into configs (#25996)
This commit is contained in:
parent
18bdd1a4f0
commit
23a8bdd522
@ -1,92 +0,0 @@
|
||||
package config
|
||||
|
||||
// Append appends one configuration to another.
|
||||
//
|
||||
// Append assumes that both configurations will not have
|
||||
// conflicting variables, resources, etc. If they do, the
|
||||
// problems will be caught in the validation phase.
|
||||
//
|
||||
// It is possible that c1, c2 on their own are not valid. For
|
||||
// example, a resource in c2 may reference a variable in c1. But
|
||||
// together, they would be valid.
|
||||
func Append(c1, c2 *Config) (*Config, error) {
|
||||
c := new(Config)
|
||||
|
||||
// Append unknown keys, but keep them unique since it is a set
|
||||
unknowns := make(map[string]struct{})
|
||||
for _, k := range c1.unknownKeys {
|
||||
_, present := unknowns[k]
|
||||
if !present {
|
||||
unknowns[k] = struct{}{}
|
||||
c.unknownKeys = append(c.unknownKeys, k)
|
||||
}
|
||||
}
|
||||
|
||||
for _, k := range c2.unknownKeys {
|
||||
_, present := unknowns[k]
|
||||
if !present {
|
||||
unknowns[k] = struct{}{}
|
||||
c.unknownKeys = append(c.unknownKeys, k)
|
||||
}
|
||||
}
|
||||
|
||||
c.Atlas = c1.Atlas
|
||||
if c2.Atlas != nil {
|
||||
c.Atlas = c2.Atlas
|
||||
}
|
||||
|
||||
// merge Terraform blocks
|
||||
if c1.Terraform != nil {
|
||||
c.Terraform = c1.Terraform
|
||||
if c2.Terraform != nil {
|
||||
c.Terraform.Merge(c2.Terraform)
|
||||
}
|
||||
} else {
|
||||
c.Terraform = c2.Terraform
|
||||
}
|
||||
|
||||
if len(c1.Modules) > 0 || len(c2.Modules) > 0 {
|
||||
c.Modules = make(
|
||||
[]*Module, 0, len(c1.Modules)+len(c2.Modules))
|
||||
c.Modules = append(c.Modules, c1.Modules...)
|
||||
c.Modules = append(c.Modules, c2.Modules...)
|
||||
}
|
||||
|
||||
if len(c1.Outputs) > 0 || len(c2.Outputs) > 0 {
|
||||
c.Outputs = make(
|
||||
[]*Output, 0, len(c1.Outputs)+len(c2.Outputs))
|
||||
c.Outputs = append(c.Outputs, c1.Outputs...)
|
||||
c.Outputs = append(c.Outputs, c2.Outputs...)
|
||||
}
|
||||
|
||||
if len(c1.ProviderConfigs) > 0 || len(c2.ProviderConfigs) > 0 {
|
||||
c.ProviderConfigs = make(
|
||||
[]*ProviderConfig,
|
||||
0, len(c1.ProviderConfigs)+len(c2.ProviderConfigs))
|
||||
c.ProviderConfigs = append(c.ProviderConfigs, c1.ProviderConfigs...)
|
||||
c.ProviderConfigs = append(c.ProviderConfigs, c2.ProviderConfigs...)
|
||||
}
|
||||
|
||||
if len(c1.Resources) > 0 || len(c2.Resources) > 0 {
|
||||
c.Resources = make(
|
||||
[]*Resource,
|
||||
0, len(c1.Resources)+len(c2.Resources))
|
||||
c.Resources = append(c.Resources, c1.Resources...)
|
||||
c.Resources = append(c.Resources, c2.Resources...)
|
||||
}
|
||||
|
||||
if len(c1.Variables) > 0 || len(c2.Variables) > 0 {
|
||||
c.Variables = make(
|
||||
[]*Variable, 0, len(c1.Variables)+len(c2.Variables))
|
||||
c.Variables = append(c.Variables, c1.Variables...)
|
||||
c.Variables = append(c.Variables, c2.Variables...)
|
||||
}
|
||||
|
||||
if len(c1.Locals) > 0 || len(c2.Locals) > 0 {
|
||||
c.Locals = make([]*Local, 0, len(c1.Locals)+len(c2.Locals))
|
||||
c.Locals = append(c.Locals, c1.Locals...)
|
||||
c.Locals = append(c.Locals, c2.Locals...)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func TestAppend(t *testing.T) {
|
||||
cases := []struct {
|
||||
c1, c2, result *Config
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
&Config{
|
||||
Atlas: &AtlasConfig{
|
||||
Name: "foo",
|
||||
},
|
||||
Modules: []*Module{
|
||||
&Module{Name: "foo"},
|
||||
},
|
||||
Outputs: []*Output{
|
||||
&Output{Name: "foo"},
|
||||
},
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Name: "foo"},
|
||||
},
|
||||
Resources: []*Resource{
|
||||
&Resource{Name: "foo"},
|
||||
},
|
||||
Variables: []*Variable{
|
||||
&Variable{Name: "foo"},
|
||||
},
|
||||
Locals: []*Local{
|
||||
&Local{Name: "foo"},
|
||||
},
|
||||
|
||||
unknownKeys: []string{"foo"},
|
||||
},
|
||||
|
||||
&Config{
|
||||
Atlas: &AtlasConfig{
|
||||
Name: "bar",
|
||||
},
|
||||
Modules: []*Module{
|
||||
&Module{Name: "bar"},
|
||||
},
|
||||
Outputs: []*Output{
|
||||
&Output{Name: "bar"},
|
||||
},
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Name: "bar"},
|
||||
},
|
||||
Resources: []*Resource{
|
||||
&Resource{Name: "bar"},
|
||||
},
|
||||
Variables: []*Variable{
|
||||
&Variable{Name: "bar"},
|
||||
},
|
||||
Locals: []*Local{
|
||||
&Local{Name: "bar"},
|
||||
},
|
||||
|
||||
unknownKeys: []string{"bar"},
|
||||
},
|
||||
|
||||
&Config{
|
||||
Atlas: &AtlasConfig{
|
||||
Name: "bar",
|
||||
},
|
||||
Modules: []*Module{
|
||||
&Module{Name: "foo"},
|
||||
&Module{Name: "bar"},
|
||||
},
|
||||
Outputs: []*Output{
|
||||
&Output{Name: "foo"},
|
||||
&Output{Name: "bar"},
|
||||
},
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Name: "foo"},
|
||||
&ProviderConfig{Name: "bar"},
|
||||
},
|
||||
Resources: []*Resource{
|
||||
&Resource{Name: "foo"},
|
||||
&Resource{Name: "bar"},
|
||||
},
|
||||
Variables: []*Variable{
|
||||
&Variable{Name: "foo"},
|
||||
&Variable{Name: "bar"},
|
||||
},
|
||||
Locals: []*Local{
|
||||
&Local{Name: "foo"},
|
||||
&Local{Name: "bar"},
|
||||
},
|
||||
|
||||
unknownKeys: []string{"foo", "bar"},
|
||||
},
|
||||
|
||||
false,
|
||||
},
|
||||
|
||||
// Terraform block
|
||||
{
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
&Config{},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
// appending configs merges terraform blocks
|
||||
{
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
Backend: &Backend{
|
||||
Type: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
Backend: &Backend{
|
||||
Type: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
|
||||
actual, err := Append(tc.c1, tc.c2)
|
||||
if err != nil != tc.err {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, tc.result) {
|
||||
t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(actual), spew.Sdump(tc.result))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
1171
config/config.go
1171
config/config.go
File diff suppressed because it is too large
Load Diff
@ -1,378 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TestString is a Stringer-like function that outputs a string that can
|
||||
// be used to easily compare multiple Config structures in unit tests.
|
||||
//
|
||||
// This function has no practical use outside of unit tests and debugging.
|
||||
func (c *Config) TestString() string {
|
||||
if c == nil {
|
||||
return "<nil config>"
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if len(c.Modules) > 0 {
|
||||
buf.WriteString("Modules:\n\n")
|
||||
buf.WriteString(modulesStr(c.Modules))
|
||||
buf.WriteString("\n\n")
|
||||
}
|
||||
|
||||
if len(c.Variables) > 0 {
|
||||
buf.WriteString("Variables:\n\n")
|
||||
buf.WriteString(variablesStr(c.Variables))
|
||||
buf.WriteString("\n\n")
|
||||
}
|
||||
|
||||
if len(c.ProviderConfigs) > 0 {
|
||||
buf.WriteString("Provider Configs:\n\n")
|
||||
buf.WriteString(providerConfigsStr(c.ProviderConfigs))
|
||||
buf.WriteString("\n\n")
|
||||
}
|
||||
|
||||
if len(c.Resources) > 0 {
|
||||
buf.WriteString("Resources:\n\n")
|
||||
buf.WriteString(resourcesStr(c.Resources))
|
||||
buf.WriteString("\n\n")
|
||||
}
|
||||
|
||||
if len(c.Outputs) > 0 {
|
||||
buf.WriteString("Outputs:\n\n")
|
||||
buf.WriteString(outputsStr(c.Outputs))
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
|
||||
func terraformStr(t *Terraform) string {
|
||||
result := ""
|
||||
|
||||
if b := t.Backend; b != nil {
|
||||
result += fmt.Sprintf("backend (%s)\n", b.Type)
|
||||
|
||||
keys := make([]string, 0, len(b.RawConfig.Raw))
|
||||
for k, _ := range b.RawConfig.Raw {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
result += fmt.Sprintf(" %s\n", k)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(result)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func outputsStr(os []*Output) string {
|
||||
ns := make([]string, 0, len(os))
|
||||
m := make(map[string]*Output)
|
||||
for _, o := range os {
|
||||
ns = append(ns, o.Name)
|
||||
m[o.Name] = o
|
||||
}
|
||||
sort.Strings(ns)
|
||||
|
||||
result := ""
|
||||
for _, n := range ns {
|
||||
o := m[n]
|
||||
|
||||
result += fmt.Sprintf("%s\n", n)
|
||||
|
||||
if len(o.DependsOn) > 0 {
|
||||
result += fmt.Sprintf(" dependsOn\n")
|
||||
for _, d := range o.DependsOn {
|
||||
result += fmt.Sprintf(" %s\n", d)
|
||||
}
|
||||
}
|
||||
|
||||
if len(o.RawConfig.Variables) > 0 {
|
||||
result += fmt.Sprintf(" vars\n")
|
||||
for _, rawV := range o.RawConfig.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)
|
||||
}
|
||||
}
|
||||
|
||||
if o.Description != "" {
|
||||
result += fmt.Sprintf(" description\n %s\n", o.Description)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(result)
|
||||
}
|
||||
|
||||
func localsStr(ls []*Local) string {
|
||||
ns := make([]string, 0, len(ls))
|
||||
m := make(map[string]*Local)
|
||||
for _, l := range ls {
|
||||
ns = append(ns, l.Name)
|
||||
m[l.Name] = l
|
||||
}
|
||||
sort.Strings(ns)
|
||||
|
||||
result := ""
|
||||
for _, n := range ns {
|
||||
l := m[n]
|
||||
|
||||
result += fmt.Sprintf("%s\n", n)
|
||||
|
||||
if len(l.RawConfig.Variables) > 0 {
|
||||
result += fmt.Sprintf(" vars\n")
|
||||
for _, rawV := range l.RawConfig.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 provider configs field into a deterministic
|
||||
// string value for comparison in tests.
|
||||
func providerConfigsStr(pcs []*ProviderConfig) string {
|
||||
result := ""
|
||||
|
||||
ns := make([]string, 0, len(pcs))
|
||||
m := make(map[string]*ProviderConfig)
|
||||
for _, n := range pcs {
|
||||
ns = append(ns, n.Name)
|
||||
m[n.Name] = n
|
||||
}
|
||||
sort.Strings(ns)
|
||||
|
||||
for _, n := range ns {
|
||||
pc := m[n]
|
||||
|
||||
result += fmt.Sprintf("%s\n", n)
|
||||
|
||||
keys := make([]string, 0, len(pc.RawConfig.Raw))
|
||||
for k, _ := range pc.RawConfig.Raw {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
result += fmt.Sprintf(" %s\n", k)
|
||||
}
|
||||
|
||||
if len(pc.RawConfig.Variables) > 0 {
|
||||
result += fmt.Sprintf(" vars\n")
|
||||
for _, rawV := range pc.RawConfig.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 {
|
||||
result := ""
|
||||
order := make([]int, 0, len(rs))
|
||||
ks := make([]string, 0, len(rs))
|
||||
mapping := make(map[string]int)
|
||||
for i, r := range rs {
|
||||
k := r.Id()
|
||||
ks = append(ks, k)
|
||||
mapping[k] = i
|
||||
}
|
||||
sort.Strings(ks)
|
||||
for _, k := range ks {
|
||||
order = append(order, mapping[k])
|
||||
}
|
||||
|
||||
for _, i := range order {
|
||||
r := rs[i]
|
||||
result += fmt.Sprintf(
|
||||
"%s (x%s)\n",
|
||||
r.Id(),
|
||||
r.RawCount.Value())
|
||||
|
||||
ks := make([]string, 0, len(r.RawConfig.Raw))
|
||||
for k, _ := range r.RawConfig.Raw {
|
||||
ks = append(ks, k)
|
||||
}
|
||||
sort.Strings(ks)
|
||||
|
||||
for _, k := range ks {
|
||||
result += fmt.Sprintf(" %s\n", k)
|
||||
}
|
||||
|
||||
if len(r.Provisioners) > 0 {
|
||||
result += fmt.Sprintf(" provisioners\n")
|
||||
for _, p := range r.Provisioners {
|
||||
when := ""
|
||||
if p.When != ProvisionerWhenCreate {
|
||||
when = fmt.Sprintf(" (%s)", p.When.String())
|
||||
}
|
||||
|
||||
result += fmt.Sprintf(" %s%s\n", p.Type, when)
|
||||
|
||||
if p.OnFailure != ProvisionerOnFailureFail {
|
||||
result += fmt.Sprintf(" on_failure = %s\n", p.OnFailure.String())
|
||||
}
|
||||
|
||||
ks := make([]string, 0, len(p.RawConfig.Raw))
|
||||
for k, _ := range p.RawConfig.Raw {
|
||||
ks = append(ks, k)
|
||||
}
|
||||
sort.Strings(ks)
|
||||
|
||||
for _, k := range ks {
|
||||
result += fmt.Sprintf(" %s\n", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.DependsOn) > 0 {
|
||||
result += fmt.Sprintf(" dependsOn\n")
|
||||
for _, d := range r.DependsOn {
|
||||
result += fmt.Sprintf(" %s\n", d)
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.RawConfig.Variables) > 0 {
|
||||
result += fmt.Sprintf(" vars\n")
|
||||
|
||||
ks := make([]string, 0, len(r.RawConfig.Variables))
|
||||
for k, _ := range r.RawConfig.Variables {
|
||||
ks = append(ks, k)
|
||||
}
|
||||
sort.Strings(ks)
|
||||
|
||||
for _, k := range ks {
|
||||
rawV := r.RawConfig.Variables[k]
|
||||
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 variables field into a deterministic
|
||||
// string value for comparison in tests.
|
||||
func variablesStr(vs []*Variable) string {
|
||||
result := ""
|
||||
ks := make([]string, 0, len(vs))
|
||||
m := make(map[string]*Variable)
|
||||
for _, v := range vs {
|
||||
ks = append(ks, v.Name)
|
||||
m[v.Name] = v
|
||||
}
|
||||
sort.Strings(ks)
|
||||
|
||||
for _, k := range ks {
|
||||
v := m[k]
|
||||
|
||||
required := ""
|
||||
if v.Required() {
|
||||
required = " (required)"
|
||||
}
|
||||
|
||||
declaredType := ""
|
||||
if v.DeclaredType != "" {
|
||||
declaredType = fmt.Sprintf(" (%s)", v.DeclaredType)
|
||||
}
|
||||
|
||||
if v.Default == nil || v.Default == "" {
|
||||
v.Default = "<>"
|
||||
}
|
||||
if v.Description == "" {
|
||||
v.Description = "<>"
|
||||
}
|
||||
|
||||
result += fmt.Sprintf(
|
||||
"%s%s%s\n %v\n %s\n",
|
||||
k,
|
||||
required,
|
||||
declaredType,
|
||||
v.Default,
|
||||
v.Description)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(result)
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
// Terraform is the Terraform meta-configuration that can be present
|
||||
// in configuration files for configuring Terraform itself.
|
||||
type Terraform struct {
|
||||
RequiredVersion string `hcl:"required_version"` // Required Terraform version (constraint)
|
||||
Backend *Backend // See Backend struct docs
|
||||
}
|
||||
|
||||
// Validate performs the validation for just the Terraform configuration.
|
||||
func (t *Terraform) Validate() []error {
|
||||
var errs []error
|
||||
|
||||
if raw := t.RequiredVersion; raw != "" {
|
||||
// Check that the value has no interpolations
|
||||
rc, err := NewRawConfig(map[string]interface{}{
|
||||
"root": raw,
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"terraform.required_version: %s", err))
|
||||
} else if len(rc.Interpolations) > 0 {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"terraform.required_version: cannot contain interpolations"))
|
||||
} else {
|
||||
// Check it is valid
|
||||
_, err := version.NewConstraint(raw)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"terraform.required_version: invalid syntax: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if t.Backend != nil {
|
||||
errs = append(errs, t.Backend.Validate()...)
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Merge t with t2.
|
||||
// Any conflicting fields are overwritten by t2.
|
||||
func (t *Terraform) Merge(t2 *Terraform) {
|
||||
if t2.RequiredVersion != "" {
|
||||
t.RequiredVersion = t2.RequiredVersion
|
||||
}
|
||||
|
||||
if t2.Backend != nil {
|
||||
t.Backend = t2.Backend
|
||||
}
|
||||
}
|
||||
|
||||
// Backend is the configuration for the "backend" to use with Terraform.
|
||||
// A backend is responsible for all major behavior of Terraform's core.
|
||||
// The abstraction layer above the core (the "backend") allows for behavior
|
||||
// such as remote operation.
|
||||
type Backend struct {
|
||||
Type string
|
||||
RawConfig *RawConfig
|
||||
|
||||
// Hash is a unique hash code representing the original configuration
|
||||
// of the backend. This won't be recomputed unless Rehash is called.
|
||||
Hash uint64
|
||||
}
|
||||
|
||||
// Rehash returns a unique content hash for this backend's configuration
|
||||
// as a uint64 value.
|
||||
func (b *Backend) Rehash() uint64 {
|
||||
// If we have no backend, the value is zero
|
||||
if b == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Use hashstructure to hash only our type with the config.
|
||||
code, err := hashstructure.Hash(map[string]interface{}{
|
||||
"type": b.Type,
|
||||
"config": b.RawConfig.Raw,
|
||||
}, nil)
|
||||
|
||||
// This should never happen since we have just some basic primitives
|
||||
// so panic if there is an error.
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
func (b *Backend) Validate() []error {
|
||||
if len(b.RawConfig.Interpolations) > 0 {
|
||||
return []error{fmt.Errorf(strings.TrimSpace(errBackendInterpolations))}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const errBackendInterpolations = `
|
||||
terraform.backend: configuration cannot contain interpolations
|
||||
|
||||
The backend configuration is loaded by Terraform extremely early, before
|
||||
the core of Terraform can be initialized. This is necessary because the backend
|
||||
dictates the behavior of that core. The core is what handles interpolation
|
||||
processing. Because of this, interpolations cannot be used in backend
|
||||
configuration.
|
||||
|
||||
If you'd like to parameterize backend configuration, we recommend using
|
||||
partial configuration with the "-backend-config" flag to "terraform init".
|
||||
`
|
@ -1,55 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackendHash(t *testing.T) {
|
||||
// WARNING: The codes below should _never_ change. If they change, it
|
||||
// means that a future TF version may falsely recognize unchanged backend
|
||||
// configuration as changed. Ultimately this should have no adverse
|
||||
// affect but it is annoying for users and should be avoided if possible.
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
Fixture string
|
||||
Code uint64
|
||||
}{
|
||||
{
|
||||
"no backend config",
|
||||
"backend-hash-empty",
|
||||
0,
|
||||
},
|
||||
|
||||
{
|
||||
"backend config with only type",
|
||||
"backend-hash-type-only",
|
||||
17852588448730441876,
|
||||
},
|
||||
|
||||
{
|
||||
"backend config with type and config",
|
||||
"backend-hash-basic",
|
||||
10288498853650209002,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
|
||||
c := testConfig(t, tc.Fixture)
|
||||
err := c.Validate()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
var actual uint64
|
||||
if c.Terraform != nil && c.Terraform.Backend != nil {
|
||||
actual = c.Terraform.Backend.Hash
|
||||
}
|
||||
if actual != tc.Code {
|
||||
t.Fatalf("bad: %d != %d", actual, tc.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,783 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/hil/ast"
|
||||
"github.com/hashicorp/terraform/helper/logging"
|
||||
)
|
||||
|
||||
// This is the directory where our test fixtures are.
|
||||
const fixtureDir = "./testdata"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
if testing.Verbose() {
|
||||
// if we're verbose, use the logging requested by TF_LOG
|
||||
logging.SetOutput()
|
||||
} else {
|
||||
// otherwise silence all logs
|
||||
log.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestConfigCopy(t *testing.T) {
|
||||
c := testConfig(t, "copy-basic")
|
||||
rOrig := c.Resources[0]
|
||||
rCopy := rOrig.Copy()
|
||||
|
||||
if rCopy.Name != rOrig.Name {
|
||||
t.Fatalf("Expected names to equal: %q <=> %q", rCopy.Name, rOrig.Name)
|
||||
}
|
||||
|
||||
if rCopy.Type != rOrig.Type {
|
||||
t.Fatalf("Expected types to equal: %q <=> %q", rCopy.Type, rOrig.Type)
|
||||
}
|
||||
|
||||
origCount := rOrig.RawCount.Config()["count"]
|
||||
rCopy.RawCount.Config()["count"] = "5"
|
||||
if rOrig.RawCount.Config()["count"] != origCount {
|
||||
t.Fatalf("Expected RawCount to be copied, but it behaves like a ref!")
|
||||
}
|
||||
|
||||
rCopy.RawConfig.Config()["newfield"] = "hello"
|
||||
if rOrig.RawConfig.Config()["newfield"] == "hello" {
|
||||
t.Fatalf("Expected RawConfig to be copied, but it behaves like a ref!")
|
||||
}
|
||||
|
||||
rCopy.Provisioners = append(rCopy.Provisioners, &Provisioner{})
|
||||
if len(rOrig.Provisioners) == len(rCopy.Provisioners) {
|
||||
t.Fatalf("Expected Provisioners to be copied, but it behaves like a ref!")
|
||||
}
|
||||
|
||||
if rCopy.Provider != rOrig.Provider {
|
||||
t.Fatalf("Expected providers to equal: %q <=> %q",
|
||||
rCopy.Provider, rOrig.Provider)
|
||||
}
|
||||
|
||||
rCopy.DependsOn[0] = "gotchya"
|
||||
if rOrig.DependsOn[0] == rCopy.DependsOn[0] {
|
||||
t.Fatalf("Expected DependsOn to be copied, but it behaves like a ref!")
|
||||
}
|
||||
|
||||
rCopy.Lifecycle.IgnoreChanges[0] = "gotchya"
|
||||
if rOrig.Lifecycle.IgnoreChanges[0] == rCopy.Lifecycle.IgnoreChanges[0] {
|
||||
t.Fatalf("Expected Lifecycle to be copied, but it behaves like a ref!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestConfigCount(t *testing.T) {
|
||||
c := testConfig(t, "count-int")
|
||||
actual, err := c.Resources[0].Count()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if actual != 5 {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigCount_string(t *testing.T) {
|
||||
c := testConfig(t, "count-string")
|
||||
actual, err := c.Resources[0].Count()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if actual != 5 {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Terraform GH-11800
|
||||
func TestConfigCount_list(t *testing.T) {
|
||||
c := testConfig(t, "count-list")
|
||||
|
||||
// The key is to interpolate so it doesn't fail parsing
|
||||
c.Resources[0].RawCount.Interpolate(map[string]ast.Variable{
|
||||
"var.list": ast.Variable{
|
||||
Value: []ast.Variable{},
|
||||
Type: ast.TypeList,
|
||||
},
|
||||
})
|
||||
|
||||
_, err := c.Resources[0].Count()
|
||||
if err == nil {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigCount_var(t *testing.T) {
|
||||
c := testConfig(t, "count-var")
|
||||
_, err := c.Resources[0].Count()
|
||||
if err == nil {
|
||||
t.Fatalf("should error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_emptyCollections(t *testing.T) {
|
||||
c := testConfig(t, "empty-collections")
|
||||
if len(c.Variables) != 3 {
|
||||
t.Fatalf("bad: expected 3 variables, got %d", len(c.Variables))
|
||||
}
|
||||
for _, variable := range c.Variables {
|
||||
switch variable.Name {
|
||||
case "empty_string":
|
||||
if variable.Default != "" {
|
||||
t.Fatalf("bad: wrong default %q for variable empty_string", variable.Default)
|
||||
}
|
||||
case "empty_map":
|
||||
if !reflect.DeepEqual(variable.Default, map[string]interface{}{}) {
|
||||
t.Fatalf("bad: wrong default %#v for variable empty_map", variable.Default)
|
||||
}
|
||||
case "empty_list":
|
||||
if !reflect.DeepEqual(variable.Default, []interface{}{}) {
|
||||
t.Fatalf("bad: wrong default %#v for variable empty_list", variable.Default)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("Unexpected variable: %s", variable.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This table test is the preferred way to test validation of configuration.
|
||||
// There are dozens of functions below which do not follow this that are
|
||||
// there mostly historically. They should be converted at some point.
|
||||
func TestConfigValidate_table(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
Fixture string
|
||||
Err bool
|
||||
ErrString string
|
||||
}{
|
||||
{
|
||||
"basic good",
|
||||
"validate-good",
|
||||
false,
|
||||
"",
|
||||
},
|
||||
|
||||
{
|
||||
"depends on module",
|
||||
"validate-depends-on-module",
|
||||
false,
|
||||
"",
|
||||
},
|
||||
|
||||
{
|
||||
"depends on non-existent module",
|
||||
"validate-depends-on-bad-module",
|
||||
true,
|
||||
"non-existent module 'foo'",
|
||||
},
|
||||
|
||||
{
|
||||
"data source with provisioners",
|
||||
"validate-data-provisioner",
|
||||
true,
|
||||
"data sources cannot have",
|
||||
},
|
||||
|
||||
{
|
||||
"basic provisioners",
|
||||
"validate-basic-provisioners",
|
||||
false,
|
||||
"",
|
||||
},
|
||||
|
||||
{
|
||||
"backend config with interpolations",
|
||||
"validate-backend-interpolate",
|
||||
true,
|
||||
"cannot contain interp",
|
||||
},
|
||||
{
|
||||
"nested types in variable default",
|
||||
"validate-var-nested",
|
||||
false,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"provider with valid version constraint",
|
||||
"provider-version",
|
||||
false,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"provider with invalid version constraint",
|
||||
"provider-version-invalid",
|
||||
true,
|
||||
"not a valid version constraint",
|
||||
},
|
||||
{
|
||||
"invalid provider name in module block",
|
||||
"validate-missing-provider",
|
||||
true,
|
||||
"cannot pass non-existent provider",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
|
||||
c := testConfig(t, tc.Fixture)
|
||||
diags := c.Validate()
|
||||
if diags.HasErrors() != tc.Err {
|
||||
t.Fatalf("err: %s", diags.Err().Error())
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
gotErr := diags.Err().Error()
|
||||
if tc.ErrString != "" && !strings.Contains(gotErr, tc.ErrString) {
|
||||
t.Fatalf("expected err to contain: %s\n\ngot: %s", tc.ErrString, gotErr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestConfigValidate_tfVersion(t *testing.T) {
|
||||
c := testConfig(t, "validate-tf-version")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_tfVersionBad(t *testing.T) {
|
||||
c := testConfig(t, "validate-bad-tf-version")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_tfVersionInterpolations(t *testing.T) {
|
||||
c := testConfig(t, "validate-tf-version-interp")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_badDependsOn(t *testing.T) {
|
||||
c := testConfig(t, "validate-bad-depends-on")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countInt(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-int")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countBadContext(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-bad-context")
|
||||
|
||||
diags := c.Validate()
|
||||
|
||||
expected := []string{
|
||||
"output \"no_count_in_output\": count variables are only valid within resources",
|
||||
"module \"no_count_in_module\": count variables are only valid within resources",
|
||||
}
|
||||
for _, exp := range expected {
|
||||
errStr := diags.Err().Error()
|
||||
if !strings.Contains(errStr, exp) {
|
||||
t.Errorf("expected: %q,\nto contain: %q", errStr, exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countCountVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-count-var")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countNotInt(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-not-int")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countUserVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-user-var")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countLocalValue(t *testing.T) {
|
||||
c := testConfig(t, "validate-local-value-count")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-var")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countVarInvalid(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-var-invalid")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countVarUnknown(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-var-unknown")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_dependsOnVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-depends-on-var")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_dupModule(t *testing.T) {
|
||||
c := testConfig(t, "validate-dup-module")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_dupResource(t *testing.T) {
|
||||
c := testConfig(t, "validate-dup-resource")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_ignoreChanges(t *testing.T) {
|
||||
c := testConfig(t, "validate-ignore-changes")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_ignoreChangesBad(t *testing.T) {
|
||||
c := testConfig(t, "validate-ignore-changes-bad")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_ignoreChangesInterpolate(t *testing.T) {
|
||||
c := testConfig(t, "validate-ignore-changes-interpolate")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_moduleNameBad(t *testing.T) {
|
||||
c := testConfig(t, "validate-module-name-bad")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_moduleSourceVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-module-source-var")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_moduleVarInt(t *testing.T) {
|
||||
c := testConfig(t, "validate-module-var-int")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_moduleVarMap(t *testing.T) {
|
||||
c := testConfig(t, "validate-module-var-map")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_moduleVarList(t *testing.T) {
|
||||
c := testConfig(t, "validate-module-var-list")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_moduleVarSelf(t *testing.T) {
|
||||
c := testConfig(t, "validate-module-var-self")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_nil(t *testing.T) {
|
||||
var c Config
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_outputBadField(t *testing.T) {
|
||||
c := testConfig(t, "validate-output-bad-field")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_outputDescription(t *testing.T) {
|
||||
c := testConfig(t, "validate-output-description")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if len(c.Outputs) != 1 {
|
||||
t.Fatalf("got %d outputs; want 1", len(c.Outputs))
|
||||
}
|
||||
if got, want := "Number 5", c.Outputs[0].Description; got != want {
|
||||
t.Fatalf("got description %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_outputDuplicate(t *testing.T) {
|
||||
c := testConfig(t, "validate-output-dup")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_pathVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-path-var")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_pathVarInvalid(t *testing.T) {
|
||||
c := testConfig(t, "validate-path-var-invalid")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_providerMulti(t *testing.T) {
|
||||
c := testConfig(t, "validate-provider-multi")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_providerMultiGood(t *testing.T) {
|
||||
c := testConfig(t, "validate-provider-multi-good")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_providerMultiRefGood(t *testing.T) {
|
||||
c := testConfig(t, "validate-provider-multi-ref-good")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_provConnSplatOther(t *testing.T) {
|
||||
c := testConfig(t, "validate-prov-conn-splat-other")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_provConnSplatSelf(t *testing.T) {
|
||||
c := testConfig(t, "validate-prov-conn-splat-self")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_provSplatOther(t *testing.T) {
|
||||
c := testConfig(t, "validate-prov-splat-other")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_provSplatSelf(t *testing.T) {
|
||||
c := testConfig(t, "validate-prov-splat-self")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_resourceProvVarSelf(t *testing.T) {
|
||||
c := testConfig(t, "validate-resource-prov-self")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_resourceVarSelf(t *testing.T) {
|
||||
c := testConfig(t, "validate-resource-self")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_unknownThing(t *testing.T) {
|
||||
c := testConfig(t, "validate-unknownthing")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_unknownResourceVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-unknown-resource-var")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_unknownResourceVar_output(t *testing.T) {
|
||||
c := testConfig(t, "validate-unknown-resource-var-output")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_unknownVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-unknownvar")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_unknownVarCount(t *testing.T) {
|
||||
c := testConfig(t, "validate-unknownvar-count")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varDefault(t *testing.T) {
|
||||
c := testConfig(t, "validate-var-default")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varDefaultListType(t *testing.T) {
|
||||
c := testConfig(t, "validate-var-default-list-type")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varDefaultInterpolate(t *testing.T) {
|
||||
c := testConfig(t, "validate-var-default-interpolate")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varDefaultInterpolateEscaped(t *testing.T) {
|
||||
c := testConfig(t, "validate-var-default-interpolate-escaped")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid, but got err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varDup(t *testing.T) {
|
||||
c := testConfig(t, "validate-var-dup")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varMultiExactNonSlice(t *testing.T) {
|
||||
c := testConfig(t, "validate-var-multi-exact-non-slice")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varMultiFunctionCall(t *testing.T) {
|
||||
c := testConfig(t, "validate-var-multi-func")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("should be valid: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varModule(t *testing.T) {
|
||||
c := testConfig(t, "validate-var-module")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varModuleInvalid(t *testing.T) {
|
||||
c := testConfig(t, "validate-var-module-invalid")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_varProviderVersionInvalid(t *testing.T) {
|
||||
c := testConfig(t, "validate-provider-version-invalid")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameRegexp(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Match bool
|
||||
}{
|
||||
{"hello", true},
|
||||
{"foo-bar", true},
|
||||
{"foo_bar", true},
|
||||
{"_hello", true},
|
||||
{"foo bar", false},
|
||||
{"foo.bar", false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
if NameRegexp.Match([]byte(tc.Input)) != tc.Match {
|
||||
t.Fatalf("Input: %s\n\nExpected: %#v", tc.Input, tc.Match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_localValuesMultiFile(t *testing.T) {
|
||||
c, err := LoadDir(filepath.Join(fixtureDir, "validate-local-multi-file"))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error during load: %s", err)
|
||||
}
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatalf("unexpected error from validate: %s", err)
|
||||
}
|
||||
if len(c.Locals) != 1 {
|
||||
t.Fatalf("got 0 locals; want 1")
|
||||
}
|
||||
if got, want := c.Locals[0].Name, "test"; got != want {
|
||||
t.Errorf("wrong local name\ngot: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderConfigName(t *testing.T) {
|
||||
pcs := []*ProviderConfig{
|
||||
&ProviderConfig{Name: "aw"},
|
||||
&ProviderConfig{Name: "aws"},
|
||||
&ProviderConfig{Name: "a"},
|
||||
&ProviderConfig{Name: "gce_"},
|
||||
}
|
||||
|
||||
n := ProviderConfigName("aws_instance", pcs)
|
||||
if n != "aws" {
|
||||
t.Fatalf("bad: %s", n)
|
||||
}
|
||||
}
|
||||
|
||||
func testConfig(t *testing.T, name string) *Config {
|
||||
c, err := LoadFile(filepath.Join(fixtureDir, name, "main.tf"))
|
||||
if err != nil {
|
||||
t.Fatalf("file: %s\n\nerr: %s", name, err)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func TestConfigDataCount(t *testing.T) {
|
||||
c := testConfig(t, "data-count")
|
||||
actual, err := c.Resources[0].Count()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if actual != 5 {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
|
||||
// we need to make sure "count" has been removed from the RawConfig, since
|
||||
// it's not a real key and won't validate.
|
||||
if _, ok := c.Resources[0].RawConfig.Raw["count"]; ok {
|
||||
t.Fatal("count key still exists in RawConfig")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigProviderVersion(t *testing.T) {
|
||||
c := testConfig(t, "provider-version")
|
||||
|
||||
if len(c.ProviderConfigs) != 1 {
|
||||
t.Fatal("expected 1 provider")
|
||||
}
|
||||
|
||||
p := c.ProviderConfigs[0]
|
||||
if p.Name != "aws" {
|
||||
t.Fatalf("expected provider name 'aws', got %q", p.Name)
|
||||
}
|
||||
|
||||
if p.Version != "0.0.1" {
|
||||
t.Fatalf("expected providers version '0.0.1', got %q", p.Version)
|
||||
}
|
||||
|
||||
if _, ok := p.RawConfig.Raw["version"]; ok {
|
||||
t.Fatal("'version' should not exist in raw config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigModuleProviders(t *testing.T) {
|
||||
c := testConfig(t, "module-providers")
|
||||
|
||||
if len(c.Modules) != 1 {
|
||||
t.Fatalf("expected 1 module, got %d", len(c.Modules))
|
||||
}
|
||||
|
||||
expected := map[string]string{
|
||||
"aws": "aws.foo",
|
||||
}
|
||||
|
||||
got := c.Modules[0].Providers
|
||||
|
||||
if !reflect.DeepEqual(expected, got) {
|
||||
t.Fatalf("exptected providers %#v, got providers %#v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateOutputErrorWarnings(t *testing.T) {
|
||||
// TODO: remove this in 0.12
|
||||
c := testConfig(t, "output-warnings")
|
||||
|
||||
diags := c.Validate()
|
||||
if diags.HasErrors() {
|
||||
t.Fatal("config should not have errors:", diags)
|
||||
}
|
||||
if len(diags) != 2 {
|
||||
t.Fatalf("should have 2 warnings, got %d:\n%s", len(diags), diags)
|
||||
}
|
||||
|
||||
// this fixture has no explicit count, and should have no warning
|
||||
c = testConfig(t, "output-no-warnings")
|
||||
if err := c.Validate(); err != nil {
|
||||
t.Fatal("config should have no warnings or errors")
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package config
|
||||
|
||||
// configTree represents a tree of configurations where the root is the
|
||||
// first file and its children are the configurations it has imported.
|
||||
type configTree struct {
|
||||
Path string
|
||||
Config *Config
|
||||
Children []*configTree
|
||||
}
|
||||
|
||||
// Flatten flattens the entire tree down to a single merged Config
|
||||
// structure.
|
||||
func (t *configTree) Flatten() (*Config, error) {
|
||||
// No children is easy: we're already merged!
|
||||
if len(t.Children) == 0 {
|
||||
return t.Config, nil
|
||||
}
|
||||
|
||||
// Depth-first, merge all the children first.
|
||||
childConfigs := make([]*Config, len(t.Children))
|
||||
for i, ct := range t.Children {
|
||||
c, err := ct.Flatten()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
childConfigs[i] = c
|
||||
}
|
||||
|
||||
// Merge all the children in order
|
||||
config := childConfigs[0]
|
||||
childConfigs = childConfigs[1:]
|
||||
for _, config2 := range childConfigs {
|
||||
var err error
|
||||
config, err = Merge(config, config2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the final merged child config with our own
|
||||
return Merge(config, t.Config)
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
)
|
||||
|
||||
// configurable is an interface that must be implemented by any configuration
|
||||
// formats of Terraform in order to return a *Config.
|
||||
type configurable interface {
|
||||
Config() (*Config, error)
|
||||
}
|
||||
|
||||
// importTree is the result of the first-pass load of the configuration
|
||||
// files. It is a tree of raw configurables and then any children (their
|
||||
// imports).
|
||||
//
|
||||
// An importTree can be turned into a configTree.
|
||||
type importTree struct {
|
||||
Path string
|
||||
Raw configurable
|
||||
Children []*importTree
|
||||
}
|
||||
|
||||
// This is the function type that must be implemented by the configuration
|
||||
// file loader to turn a single file into a configurable and any additional
|
||||
// imports.
|
||||
type fileLoaderFunc func(path string) (configurable, []string, error)
|
||||
|
||||
// Set this to a non-empty value at link time to enable the HCL2 experiment.
|
||||
// This is not currently enabled for release builds.
|
||||
//
|
||||
// For example:
|
||||
// go install -ldflags="-X github.com/hashicorp/terraform/config.enableHCL2Experiment=true" github.com/hashicorp/terraform
|
||||
var enableHCL2Experiment = ""
|
||||
|
||||
// loadTree takes a single file and loads the entire importTree for that
|
||||
// file. This function detects what kind of configuration file it is an
|
||||
// executes the proper fileLoaderFunc.
|
||||
func loadTree(root string) (*importTree, error) {
|
||||
var f fileLoaderFunc
|
||||
|
||||
// HCL2 experiment is currently activated at build time via the linker.
|
||||
// See the comment on this variable for more information.
|
||||
if enableHCL2Experiment == "" {
|
||||
// Main-line behavior: always use the original HCL parser
|
||||
switch ext(root) {
|
||||
case ".tf", ".tf.json":
|
||||
f = loadFileHcl
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
// Experimental behavior: use the HCL2 parser if the opt-in comment
|
||||
// is present.
|
||||
switch ext(root) {
|
||||
case ".tf":
|
||||
// We need to sniff the file for the opt-in comment line to decide
|
||||
// if the file is participating in the HCL2 experiment.
|
||||
cf, err := os.Open(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sc := bufio.NewScanner(cf)
|
||||
for sc.Scan() {
|
||||
if sc.Text() == "#terraform:hcl2" {
|
||||
f = globalHCL2Loader.loadFile
|
||||
}
|
||||
}
|
||||
if f == nil {
|
||||
f = loadFileHcl
|
||||
}
|
||||
case ".tf.json":
|
||||
f = loadFileHcl
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf(
|
||||
"%s: unknown configuration format. Use '.tf' or '.tf.json' extension",
|
||||
root)
|
||||
}
|
||||
|
||||
c, imps, err := f(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
children := make([]*importTree, len(imps))
|
||||
for i, imp := range imps {
|
||||
t, err := loadTree(imp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
children[i] = t
|
||||
}
|
||||
|
||||
return &importTree{
|
||||
Path: root,
|
||||
Raw: c,
|
||||
Children: children,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close releases any resources we might be holding open for the importTree.
|
||||
//
|
||||
// This can safely be called even while ConfigTree results are alive. The
|
||||
// importTree is not bound to these.
|
||||
func (t *importTree) Close() error {
|
||||
if c, ok := t.Raw.(io.Closer); ok {
|
||||
c.Close()
|
||||
}
|
||||
for _, ct := range t.Children {
|
||||
ct.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigTree traverses the importTree and turns each node into a *Config
|
||||
// object, ultimately returning a *configTree.
|
||||
func (t *importTree) ConfigTree() (*configTree, error) {
|
||||
config, err := t.Raw.Config()
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf(fmt.Sprintf("Error loading %s: {{err}}", t.Path), err)
|
||||
}
|
||||
|
||||
// Build our result
|
||||
result := &configTree{
|
||||
Path: t.Path,
|
||||
Config: config,
|
||||
}
|
||||
|
||||
// Build the config trees for the children
|
||||
result.Children = make([]*configTree, len(t.Children))
|
||||
for i, ct := range t.Children {
|
||||
t, err := ct.ConfigTree()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result.Children[i] = t
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestImportTreeHCL2Experiment(t *testing.T) {
|
||||
// Can only run this test if we're built with the experiment enabled.
|
||||
// Enable this test by passing the following option to "go test":
|
||||
// -ldflags="-X github.com/hashicorp/terraform/config.enableHCL2Experiment=true"
|
||||
// See the comment associated with this flag variable for more information.
|
||||
if enableHCL2Experiment == "" {
|
||||
t.Skip("HCL2 experiment is not enabled")
|
||||
}
|
||||
|
||||
t.Run("HCL not opted in", func(t *testing.T) {
|
||||
// .tf file without opt-in should use the old HCL parser
|
||||
imp, err := loadTree("testdata/hcl2-experiment-switch/not-opted-in.tf")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tree, err := imp.ConfigTree()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading not-opted-in.tf: %s", err)
|
||||
}
|
||||
|
||||
cfg := tree.Config
|
||||
if got, want := len(cfg.Locals), 1; got != want {
|
||||
t.Fatalf("wrong number of locals %#v; want %#v", got, want)
|
||||
}
|
||||
if cfg.Locals[0].RawConfig.Raw == nil {
|
||||
// Having RawConfig.Raw indicates the old loader
|
||||
t.Fatal("local has no RawConfig.Raw")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("HCL opted in", func(t *testing.T) {
|
||||
// .tf file with opt-in should use the new HCL2 parser
|
||||
imp, err := loadTree("testdata/hcl2-experiment-switch/opted-in.tf")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tree, err := imp.ConfigTree()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading opted-in.tf: %s", err)
|
||||
}
|
||||
|
||||
cfg := tree.Config
|
||||
if got, want := len(cfg.Locals), 1; got != want {
|
||||
t.Fatalf("wrong number of locals %#v; want %#v", got, want)
|
||||
}
|
||||
if cfg.Locals[0].RawConfig.Body == nil {
|
||||
// Having RawConfig.Body indicates the new loader
|
||||
t.Fatal("local has no RawConfig.Body")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("JSON ineligible", func(t *testing.T) {
|
||||
// .tf.json file should always use the old HCL parser
|
||||
imp, err := loadTree("testdata/hcl2-experiment-switch/not-eligible.tf.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tree, err := imp.ConfigTree()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading not-eligible.tf.json: %s", err)
|
||||
}
|
||||
|
||||
cfg := tree.Config
|
||||
if got, want := len(cfg.Locals), 1; got != want {
|
||||
t.Fatalf("wrong number of locals %#v; want %#v", got, want)
|
||||
}
|
||||
if cfg.Locals[0].RawConfig.Raw == nil {
|
||||
// Having RawConfig.Raw indicates the old loader
|
||||
t.Fatal("local has no RawConfig.Raw")
|
||||
}
|
||||
})
|
||||
}
|
212
config/loader.go
212
config/loader.go
@ -1,212 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl"
|
||||
)
|
||||
|
||||
// ErrNoConfigsFound is the error returned by LoadDir if no
|
||||
// Terraform configuration files were found in the given directory.
|
||||
type ErrNoConfigsFound struct {
|
||||
Dir string
|
||||
}
|
||||
|
||||
func (e ErrNoConfigsFound) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"No Terraform configuration files found in directory: %s",
|
||||
e.Dir)
|
||||
}
|
||||
|
||||
// LoadJSON loads a single Terraform configuration from a given JSON document.
|
||||
//
|
||||
// The document must be a complete Terraform configuration. This function will
|
||||
// NOT try to load any additional modules so only the given document is loaded.
|
||||
func LoadJSON(raw json.RawMessage) (*Config, error) {
|
||||
obj, err := hcl.Parse(string(raw))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error parsing JSON document as HCL: %s", err)
|
||||
}
|
||||
|
||||
// Start building the result
|
||||
hclConfig := &hclConfigurable{
|
||||
Root: obj,
|
||||
}
|
||||
|
||||
return hclConfig.Config()
|
||||
}
|
||||
|
||||
// LoadFile loads the Terraform configuration from a given file.
|
||||
//
|
||||
// This file can be any format that Terraform recognizes, and import any
|
||||
// other format that Terraform recognizes.
|
||||
func LoadFile(path string) (*Config, error) {
|
||||
importTree, err := loadTree(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configTree, err := importTree.ConfigTree()
|
||||
|
||||
// Close the importTree now so that we can clear resources as quickly
|
||||
// as possible.
|
||||
importTree.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return configTree.Flatten()
|
||||
}
|
||||
|
||||
// LoadDir loads all the Terraform configuration files in a single
|
||||
// directory and appends them together.
|
||||
//
|
||||
// Special files known as "override files" can also be present, which
|
||||
// are merged into the loaded configuration. That is, the non-override
|
||||
// files are loaded first to create the configuration. Then, the overrides
|
||||
// are merged into the configuration to create the final configuration.
|
||||
//
|
||||
// Files are loaded in lexical order.
|
||||
func LoadDir(root string) (*Config, error) {
|
||||
files, overrides, err := dirFiles(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(files) == 0 && len(overrides) == 0 {
|
||||
return nil, &ErrNoConfigsFound{Dir: root}
|
||||
}
|
||||
|
||||
// Determine the absolute path to the directory.
|
||||
rootAbs, err := filepath.Abs(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result *Config
|
||||
|
||||
// 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 := LoadFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
result, err = Append(result, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
result = c
|
||||
}
|
||||
}
|
||||
if len(files) == 0 {
|
||||
result = &Config{}
|
||||
}
|
||||
|
||||
// Load all the overrides, and merge them into the config
|
||||
for _, f := range overrides {
|
||||
c, err := LoadFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err = Merge(result, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the directory
|
||||
result.Dir = rootAbs
|
||||
|
||||
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 ""
|
||||
}
|
||||
}
|
||||
|
||||
func dirFiles(dir string) ([]string, []string, error) {
|
||||
f, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
return nil, nil, fmt.Errorf(
|
||||
"configuration path must be a directory: %s",
|
||||
dir)
|
||||
}
|
||||
|
||||
var files, overrides []string
|
||||
err = nil
|
||||
for err != io.EOF {
|
||||
var fis []os.FileInfo
|
||||
fis, err = f.Readdir(128)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, 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 == "" || IsIgnoredFile(name) {
|
||||
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(dir, name)
|
||||
if override {
|
||||
overrides = append(overrides, path)
|
||||
} else {
|
||||
files = append(files, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return files, overrides, nil
|
||||
}
|
||||
|
||||
// IsIgnoredFile returns true or false depending on whether the
|
||||
// provided file name is a file that should be ignored.
|
||||
func IsIgnoredFile(name string) bool {
|
||||
return strings.HasPrefix(name, ".") || // Unix-like hidden files
|
||||
strings.HasSuffix(name, "~") || // vim
|
||||
strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#") // emacs
|
||||
}
|
1270
config/loader_hcl.go
1270
config/loader_hcl.go
File diff suppressed because it is too large
Load Diff
@ -1,473 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
hcl2 "github.com/hashicorp/hcl/v2"
|
||||
gohcl2 "github.com/hashicorp/hcl/v2/gohcl"
|
||||
hcl2parse "github.com/hashicorp/hcl/v2/hclparse"
|
||||
"github.com/hashicorp/terraform/configs/hcl2shim"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// hcl2Configurable is an implementation of configurable that knows
|
||||
// how to turn a HCL Body into a *Config object.
|
||||
type hcl2Configurable struct {
|
||||
SourceFilename string
|
||||
Body hcl2.Body
|
||||
}
|
||||
|
||||
// hcl2Loader is a wrapper around a HCL parser that provides a fileLoaderFunc.
|
||||
type hcl2Loader struct {
|
||||
Parser *hcl2parse.Parser
|
||||
}
|
||||
|
||||
// For the moment we'll just have a global loader since we don't have anywhere
|
||||
// better to stash this.
|
||||
// TODO: refactor the loader API so that it uses some sort of object we can
|
||||
// stash the parser inside.
|
||||
var globalHCL2Loader = newHCL2Loader()
|
||||
|
||||
// newHCL2Loader creates a new hcl2Loader containing a new HCL Parser.
|
||||
//
|
||||
// HCL parsers retain information about files that are loaded to aid in
|
||||
// producing diagnostic messages, so all files within a single configuration
|
||||
// should be loaded with the same parser to ensure the availability of
|
||||
// full diagnostic information.
|
||||
func newHCL2Loader() hcl2Loader {
|
||||
return hcl2Loader{
|
||||
Parser: hcl2parse.NewParser(),
|
||||
}
|
||||
}
|
||||
|
||||
// loadFile is a fileLoaderFunc that knows how to read a HCL2 file and turn it
|
||||
// into a hcl2Configurable.
|
||||
func (l hcl2Loader) loadFile(filename string) (configurable, []string, error) {
|
||||
var f *hcl2.File
|
||||
var diags hcl2.Diagnostics
|
||||
if strings.HasSuffix(filename, ".json") {
|
||||
f, diags = l.Parser.ParseJSONFile(filename)
|
||||
} else {
|
||||
f, diags = l.Parser.ParseHCLFile(filename)
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
// Return diagnostics as an error; callers may type-assert this to
|
||||
// recover the original diagnostics, if it doesn't end up wrapped
|
||||
// in another error.
|
||||
return nil, nil, diags
|
||||
}
|
||||
|
||||
return &hcl2Configurable{
|
||||
SourceFilename: filename,
|
||||
Body: f.Body,
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
func (t *hcl2Configurable) Config() (*Config, error) {
|
||||
config := &Config{}
|
||||
|
||||
// these structs are used only for the initial shallow decoding; we'll
|
||||
// expand this into the main, public-facing config structs afterwards.
|
||||
type atlas struct {
|
||||
Name string `hcl:"name"`
|
||||
Include *[]string `hcl:"include"`
|
||||
Exclude *[]string `hcl:"exclude"`
|
||||
}
|
||||
type provider struct {
|
||||
Name string `hcl:"name,label"`
|
||||
Alias *string `hcl:"alias,attr"`
|
||||
Version *string `hcl:"version,attr"`
|
||||
Config hcl2.Body `hcl:",remain"`
|
||||
}
|
||||
type module struct {
|
||||
Name string `hcl:"name,label"`
|
||||
Source string `hcl:"source,attr"`
|
||||
Version *string `hcl:"version,attr"`
|
||||
Providers *map[string]string `hcl:"providers,attr"`
|
||||
Config hcl2.Body `hcl:",remain"`
|
||||
}
|
||||
type resourceLifecycle struct {
|
||||
CreateBeforeDestroy *bool `hcl:"create_before_destroy,attr"`
|
||||
PreventDestroy *bool `hcl:"prevent_destroy,attr"`
|
||||
IgnoreChanges *[]string `hcl:"ignore_changes,attr"`
|
||||
}
|
||||
type connection struct {
|
||||
Config hcl2.Body `hcl:",remain"`
|
||||
}
|
||||
type provisioner struct {
|
||||
Type string `hcl:"type,label"`
|
||||
|
||||
When *string `hcl:"when,attr"`
|
||||
OnFailure *string `hcl:"on_failure,attr"`
|
||||
|
||||
Connection *connection `hcl:"connection,block"`
|
||||
Config hcl2.Body `hcl:",remain"`
|
||||
}
|
||||
type managedResource struct {
|
||||
Type string `hcl:"type,label"`
|
||||
Name string `hcl:"name,label"`
|
||||
|
||||
CountExpr hcl2.Expression `hcl:"count,attr"`
|
||||
Provider *string `hcl:"provider,attr"`
|
||||
DependsOn *[]string `hcl:"depends_on,attr"`
|
||||
|
||||
Lifecycle *resourceLifecycle `hcl:"lifecycle,block"`
|
||||
Provisioners []provisioner `hcl:"provisioner,block"`
|
||||
Connection *connection `hcl:"connection,block"`
|
||||
|
||||
Config hcl2.Body `hcl:",remain"`
|
||||
}
|
||||
type dataResource struct {
|
||||
Type string `hcl:"type,label"`
|
||||
Name string `hcl:"name,label"`
|
||||
|
||||
CountExpr hcl2.Expression `hcl:"count,attr"`
|
||||
Provider *string `hcl:"provider,attr"`
|
||||
DependsOn *[]string `hcl:"depends_on,attr"`
|
||||
|
||||
Config hcl2.Body `hcl:",remain"`
|
||||
}
|
||||
type variable struct {
|
||||
Name string `hcl:"name,label"`
|
||||
|
||||
DeclaredType *string `hcl:"type,attr"`
|
||||
Default *cty.Value `hcl:"default,attr"`
|
||||
Description *string `hcl:"description,attr"`
|
||||
Sensitive *bool `hcl:"sensitive,attr"`
|
||||
}
|
||||
type output struct {
|
||||
Name string `hcl:"name,label"`
|
||||
|
||||
ValueExpr hcl2.Expression `hcl:"value,attr"`
|
||||
DependsOn *[]string `hcl:"depends_on,attr"`
|
||||
Description *string `hcl:"description,attr"`
|
||||
Sensitive *bool `hcl:"sensitive,attr"`
|
||||
}
|
||||
type locals struct {
|
||||
Definitions hcl2.Attributes `hcl:",remain"`
|
||||
}
|
||||
type backend struct {
|
||||
Type string `hcl:"type,label"`
|
||||
Config hcl2.Body `hcl:",remain"`
|
||||
}
|
||||
type terraform struct {
|
||||
RequiredVersion *string `hcl:"required_version,attr"`
|
||||
Backend *backend `hcl:"backend,block"`
|
||||
}
|
||||
type topLevel struct {
|
||||
Atlas *atlas `hcl:"atlas,block"`
|
||||
Datas []dataResource `hcl:"data,block"`
|
||||
Modules []module `hcl:"module,block"`
|
||||
Outputs []output `hcl:"output,block"`
|
||||
Providers []provider `hcl:"provider,block"`
|
||||
Resources []managedResource `hcl:"resource,block"`
|
||||
Terraform *terraform `hcl:"terraform,block"`
|
||||
Variables []variable `hcl:"variable,block"`
|
||||
Locals []*locals `hcl:"locals,block"`
|
||||
}
|
||||
|
||||
var raw topLevel
|
||||
diags := gohcl2.DecodeBody(t.Body, nil, &raw)
|
||||
if diags.HasErrors() {
|
||||
// Do some minimal decoding to see if we can at least get the
|
||||
// required Terraform version, which might help explain why we
|
||||
// couldn't parse the rest.
|
||||
if raw.Terraform != nil && raw.Terraform.RequiredVersion != nil {
|
||||
config.Terraform = &Terraform{
|
||||
RequiredVersion: *raw.Terraform.RequiredVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// We return the diags as an implementation of error, which the
|
||||
// caller than then type-assert if desired to recover the individual
|
||||
// diagnostics.
|
||||
// FIXME: The current API gives us no way to return warnings in the
|
||||
// absence of any errors.
|
||||
return config, diags
|
||||
}
|
||||
|
||||
if raw.Terraform != nil {
|
||||
var reqdVersion string
|
||||
var backend *Backend
|
||||
|
||||
if raw.Terraform.RequiredVersion != nil {
|
||||
reqdVersion = *raw.Terraform.RequiredVersion
|
||||
}
|
||||
if raw.Terraform.Backend != nil {
|
||||
backend = new(Backend)
|
||||
backend.Type = raw.Terraform.Backend.Type
|
||||
|
||||
// We don't permit interpolations or nested blocks inside the
|
||||
// backend config, so we can decode the config early here and
|
||||
// get direct access to the values, which is important for the
|
||||
// config hashing to work as expected.
|
||||
var config map[string]string
|
||||
configDiags := gohcl2.DecodeBody(raw.Terraform.Backend.Config, nil, &config)
|
||||
diags = append(diags, configDiags...)
|
||||
|
||||
raw := make(map[string]interface{}, len(config))
|
||||
for k, v := range config {
|
||||
raw[k] = v
|
||||
}
|
||||
|
||||
var err error
|
||||
backend.RawConfig, err = NewRawConfig(raw)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl2.Diagnostic{
|
||||
Severity: hcl2.DiagError,
|
||||
Summary: "Invalid backend configuration",
|
||||
Detail: fmt.Sprintf("Error in backend configuration: %s", err),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
config.Terraform = &Terraform{
|
||||
RequiredVersion: reqdVersion,
|
||||
Backend: backend,
|
||||
}
|
||||
}
|
||||
|
||||
if raw.Atlas != nil {
|
||||
var include, exclude []string
|
||||
if raw.Atlas.Include != nil {
|
||||
include = *raw.Atlas.Include
|
||||
}
|
||||
if raw.Atlas.Exclude != nil {
|
||||
exclude = *raw.Atlas.Exclude
|
||||
}
|
||||
config.Atlas = &AtlasConfig{
|
||||
Name: raw.Atlas.Name,
|
||||
Include: include,
|
||||
Exclude: exclude,
|
||||
}
|
||||
}
|
||||
|
||||
for _, rawM := range raw.Modules {
|
||||
m := &Module{
|
||||
Name: rawM.Name,
|
||||
Source: rawM.Source,
|
||||
RawConfig: NewRawConfigHCL2(rawM.Config),
|
||||
}
|
||||
|
||||
if rawM.Version != nil {
|
||||
m.Version = *rawM.Version
|
||||
}
|
||||
|
||||
if rawM.Providers != nil {
|
||||
m.Providers = *rawM.Providers
|
||||
}
|
||||
|
||||
config.Modules = append(config.Modules, m)
|
||||
}
|
||||
|
||||
for _, rawV := range raw.Variables {
|
||||
v := &Variable{
|
||||
Name: rawV.Name,
|
||||
}
|
||||
if rawV.DeclaredType != nil {
|
||||
v.DeclaredType = *rawV.DeclaredType
|
||||
}
|
||||
if rawV.Default != nil {
|
||||
v.Default = hcl2shim.ConfigValueFromHCL2(*rawV.Default)
|
||||
}
|
||||
if rawV.Description != nil {
|
||||
v.Description = *rawV.Description
|
||||
}
|
||||
|
||||
config.Variables = append(config.Variables, v)
|
||||
}
|
||||
|
||||
for _, rawO := range raw.Outputs {
|
||||
o := &Output{
|
||||
Name: rawO.Name,
|
||||
}
|
||||
|
||||
if rawO.Description != nil {
|
||||
o.Description = *rawO.Description
|
||||
}
|
||||
if rawO.DependsOn != nil {
|
||||
o.DependsOn = *rawO.DependsOn
|
||||
}
|
||||
if rawO.Sensitive != nil {
|
||||
o.Sensitive = *rawO.Sensitive
|
||||
}
|
||||
|
||||
// The result is expected to be a map like map[string]interface{}{"value": something},
|
||||
// so we'll fake that with our hcl2shim.SingleAttrBody shim.
|
||||
o.RawConfig = NewRawConfigHCL2(hcl2shim.SingleAttrBody{
|
||||
Name: "value",
|
||||
Expr: rawO.ValueExpr,
|
||||
})
|
||||
|
||||
config.Outputs = append(config.Outputs, o)
|
||||
}
|
||||
|
||||
for _, rawR := range raw.Resources {
|
||||
r := &Resource{
|
||||
Mode: ManagedResourceMode,
|
||||
Type: rawR.Type,
|
||||
Name: rawR.Name,
|
||||
}
|
||||
if rawR.Lifecycle != nil {
|
||||
var l ResourceLifecycle
|
||||
if rawR.Lifecycle.CreateBeforeDestroy != nil {
|
||||
l.CreateBeforeDestroy = *rawR.Lifecycle.CreateBeforeDestroy
|
||||
}
|
||||
if rawR.Lifecycle.PreventDestroy != nil {
|
||||
l.PreventDestroy = *rawR.Lifecycle.PreventDestroy
|
||||
}
|
||||
if rawR.Lifecycle.IgnoreChanges != nil {
|
||||
l.IgnoreChanges = *rawR.Lifecycle.IgnoreChanges
|
||||
}
|
||||
r.Lifecycle = l
|
||||
}
|
||||
if rawR.Provider != nil {
|
||||
r.Provider = *rawR.Provider
|
||||
}
|
||||
if rawR.DependsOn != nil {
|
||||
r.DependsOn = *rawR.DependsOn
|
||||
}
|
||||
|
||||
var defaultConnInfo *RawConfig
|
||||
if rawR.Connection != nil {
|
||||
defaultConnInfo = NewRawConfigHCL2(rawR.Connection.Config)
|
||||
}
|
||||
|
||||
for _, rawP := range rawR.Provisioners {
|
||||
p := &Provisioner{
|
||||
Type: rawP.Type,
|
||||
}
|
||||
|
||||
switch {
|
||||
case rawP.When == nil:
|
||||
p.When = ProvisionerWhenCreate
|
||||
case *rawP.When == "create":
|
||||
p.When = ProvisionerWhenCreate
|
||||
case *rawP.When == "destroy":
|
||||
p.When = ProvisionerWhenDestroy
|
||||
default:
|
||||
p.When = ProvisionerWhenInvalid
|
||||
}
|
||||
|
||||
switch {
|
||||
case rawP.OnFailure == nil:
|
||||
p.OnFailure = ProvisionerOnFailureFail
|
||||
case *rawP.When == "fail":
|
||||
p.OnFailure = ProvisionerOnFailureFail
|
||||
case *rawP.When == "continue":
|
||||
p.OnFailure = ProvisionerOnFailureContinue
|
||||
default:
|
||||
p.OnFailure = ProvisionerOnFailureInvalid
|
||||
}
|
||||
|
||||
if rawP.Connection != nil {
|
||||
p.ConnInfo = NewRawConfigHCL2(rawP.Connection.Config)
|
||||
} else {
|
||||
p.ConnInfo = defaultConnInfo
|
||||
}
|
||||
|
||||
p.RawConfig = NewRawConfigHCL2(rawP.Config)
|
||||
|
||||
r.Provisioners = append(r.Provisioners, p)
|
||||
}
|
||||
|
||||
// The old loader records the count expression as a weird RawConfig with
|
||||
// a single-element map inside. Since the rest of the world is assuming
|
||||
// that, we'll mimic it here.
|
||||
{
|
||||
countBody := hcl2shim.SingleAttrBody{
|
||||
Name: "count",
|
||||
Expr: rawR.CountExpr,
|
||||
}
|
||||
|
||||
r.RawCount = NewRawConfigHCL2(countBody)
|
||||
r.RawCount.Key = "count"
|
||||
}
|
||||
|
||||
r.RawConfig = NewRawConfigHCL2(rawR.Config)
|
||||
|
||||
config.Resources = append(config.Resources, r)
|
||||
|
||||
}
|
||||
|
||||
for _, rawR := range raw.Datas {
|
||||
r := &Resource{
|
||||
Mode: DataResourceMode,
|
||||
Type: rawR.Type,
|
||||
Name: rawR.Name,
|
||||
}
|
||||
|
||||
if rawR.Provider != nil {
|
||||
r.Provider = *rawR.Provider
|
||||
}
|
||||
if rawR.DependsOn != nil {
|
||||
r.DependsOn = *rawR.DependsOn
|
||||
}
|
||||
|
||||
// The old loader records the count expression as a weird RawConfig with
|
||||
// a single-element map inside. Since the rest of the world is assuming
|
||||
// that, we'll mimic it here.
|
||||
{
|
||||
countBody := hcl2shim.SingleAttrBody{
|
||||
Name: "count",
|
||||
Expr: rawR.CountExpr,
|
||||
}
|
||||
|
||||
r.RawCount = NewRawConfigHCL2(countBody)
|
||||
r.RawCount.Key = "count"
|
||||
}
|
||||
|
||||
r.RawConfig = NewRawConfigHCL2(rawR.Config)
|
||||
|
||||
config.Resources = append(config.Resources, r)
|
||||
}
|
||||
|
||||
for _, rawP := range raw.Providers {
|
||||
p := &ProviderConfig{
|
||||
Name: rawP.Name,
|
||||
}
|
||||
|
||||
if rawP.Alias != nil {
|
||||
p.Alias = *rawP.Alias
|
||||
}
|
||||
if rawP.Version != nil {
|
||||
p.Version = *rawP.Version
|
||||
}
|
||||
|
||||
// The result is expected to be a map like map[string]interface{}{"value": something},
|
||||
// so we'll fake that with our hcl2shim.SingleAttrBody shim.
|
||||
p.RawConfig = NewRawConfigHCL2(rawP.Config)
|
||||
|
||||
config.ProviderConfigs = append(config.ProviderConfigs, p)
|
||||
}
|
||||
|
||||
for _, rawL := range raw.Locals {
|
||||
names := make([]string, 0, len(rawL.Definitions))
|
||||
for n := range rawL.Definitions {
|
||||
names = append(names, n)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, n := range names {
|
||||
attr := rawL.Definitions[n]
|
||||
l := &Local{
|
||||
Name: n,
|
||||
RawConfig: NewRawConfigHCL2(hcl2shim.SingleAttrBody{
|
||||
Name: "value",
|
||||
Expr: attr.Expr,
|
||||
}),
|
||||
}
|
||||
config.Locals = append(config.Locals, l)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: The current API gives us no way to return warnings in the
|
||||
// absence of any errors.
|
||||
var err error
|
||||
if diags.HasErrors() {
|
||||
err = diags
|
||||
}
|
||||
|
||||
return config, err
|
||||
}
|
@ -1,510 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
hcl2 "github.com/hashicorp/hcl/v2"
|
||||
gohcl2 "github.com/hashicorp/hcl/v2/gohcl"
|
||||
)
|
||||
|
||||
func TestHCL2ConfigurableConfigurable(t *testing.T) {
|
||||
var _ configurable = new(hcl2Configurable)
|
||||
}
|
||||
|
||||
func TestHCL2Basic(t *testing.T) {
|
||||
loader := globalHCL2Loader
|
||||
cbl, _, err := loader.loadFile("testdata/basic-hcl2.tf")
|
||||
if err != nil {
|
||||
if diags, isDiags := err.(hcl2.Diagnostics); isDiags {
|
||||
for _, diag := range diags {
|
||||
t.Logf("- %s", diag.Error())
|
||||
}
|
||||
t.Fatalf("unexpected diagnostics in load")
|
||||
} else {
|
||||
t.Fatalf("unexpected error in load: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := cbl.Config()
|
||||
if err != nil {
|
||||
if diags, isDiags := err.(hcl2.Diagnostics); isDiags {
|
||||
for _, diag := range diags {
|
||||
t.Logf("- %s", diag.Error())
|
||||
}
|
||||
t.Fatalf("unexpected diagnostics in decode")
|
||||
} else {
|
||||
t.Fatalf("unexpected error in decode: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately the config structure isn't DeepEqual-friendly because
|
||||
// of all the nested RawConfig, etc structures, so we'll need to
|
||||
// hand-assert each item.
|
||||
|
||||
// The "terraform" block
|
||||
if cfg.Terraform == nil {
|
||||
t.Fatalf("Terraform field is nil")
|
||||
}
|
||||
if got, want := cfg.Terraform.RequiredVersion, "foo"; got != want {
|
||||
t.Errorf("wrong Terraform.RequiredVersion %q; want %q", got, want)
|
||||
}
|
||||
if cfg.Terraform.Backend == nil {
|
||||
t.Fatalf("Terraform.Backend is nil")
|
||||
}
|
||||
if got, want := cfg.Terraform.Backend.Type, "baz"; got != want {
|
||||
t.Errorf("wrong Terraform.Backend.Type %q; want %q", got, want)
|
||||
}
|
||||
if got, want := cfg.Terraform.Backend.RawConfig.Raw, map[string]interface{}{"something": "nothing"}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong Terraform.Backend.RawConfig.Raw %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
// The "atlas" block
|
||||
if cfg.Atlas == nil {
|
||||
t.Fatalf("Atlas field is nil")
|
||||
}
|
||||
if got, want := cfg.Atlas.Name, "example/foo"; got != want {
|
||||
t.Errorf("wrong Atlas.Name %q; want %q", got, want)
|
||||
}
|
||||
|
||||
// "module" blocks
|
||||
if got, want := len(cfg.Modules), 1; got != want {
|
||||
t.Errorf("Modules slice has wrong length %#v; want %#v", got, want)
|
||||
} else {
|
||||
m := cfg.Modules[0]
|
||||
if got, want := m.Name, "child"; got != want {
|
||||
t.Errorf("wrong Modules[0].Name %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := m.Source, "./baz"; got != want {
|
||||
t.Errorf("wrong Modules[0].Source %#v; want %#v", got, want)
|
||||
}
|
||||
want := map[string]string{"toasty": "true"}
|
||||
var got map[string]string
|
||||
gohcl2.DecodeBody(m.RawConfig.Body, nil, &got)
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong Modules[0].RawConfig.Body %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// "resource" blocks
|
||||
if got, want := len(cfg.Resources), 5; got != want {
|
||||
t.Errorf("Resources slice has wrong length %#v; want %#v", got, want)
|
||||
} else {
|
||||
{
|
||||
r := cfg.Resources[0]
|
||||
|
||||
if got, want := r.Id(), "aws_security_group.firewall"; got != want {
|
||||
t.Errorf("wrong Resources[0].Id() %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
wantConfig := map[string]string{}
|
||||
var gotConfig map[string]string
|
||||
gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig)
|
||||
if !reflect.DeepEqual(gotConfig, wantConfig) {
|
||||
t.Errorf("wrong Resources[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
|
||||
}
|
||||
|
||||
wantCount := map[string]string{"count": "5"}
|
||||
var gotCount map[string]string
|
||||
gohcl2.DecodeBody(r.RawCount.Body, nil, &gotCount)
|
||||
if !reflect.DeepEqual(gotCount, wantCount) {
|
||||
t.Errorf("wrong Resources[0].RawCount.Body %#v; want %#v", gotCount, wantCount)
|
||||
}
|
||||
if got, want := r.RawCount.Key, "count"; got != want {
|
||||
t.Errorf("wrong Resources[0].RawCount.Key %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
if got, want := len(r.Provisioners), 0; got != want {
|
||||
t.Errorf("wrong Resources[0].Provisioners length %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := len(r.DependsOn), 0; got != want {
|
||||
t.Errorf("wrong Resources[0].DependsOn length %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := r.Provider, "another"; got != want {
|
||||
t.Errorf("wrong Resources[0].Provider %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := r.Lifecycle, (ResourceLifecycle{}); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong Resources[0].Lifecycle %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
{
|
||||
r := cfg.Resources[1]
|
||||
|
||||
if got, want := r.Id(), "aws_instance.web"; got != want {
|
||||
t.Errorf("wrong Resources[1].Id() %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := r.Provider, ""; got != want {
|
||||
t.Errorf("wrong Resources[1].Provider %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
if got, want := len(r.Provisioners), 1; got != want {
|
||||
t.Errorf("wrong Resources[1].Provisioners length %#v; want %#v", got, want)
|
||||
} else {
|
||||
p := r.Provisioners[0]
|
||||
|
||||
if got, want := p.Type, "file"; got != want {
|
||||
t.Errorf("wrong Resources[1].Provisioners[0].Type %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
wantConfig := map[string]string{
|
||||
"source": "foo",
|
||||
"destination": "bar",
|
||||
}
|
||||
var gotConfig map[string]string
|
||||
gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig)
|
||||
if !reflect.DeepEqual(gotConfig, wantConfig) {
|
||||
t.Errorf("wrong Resources[1].Provisioners[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
|
||||
}
|
||||
|
||||
wantConn := map[string]string{
|
||||
"default": "true",
|
||||
}
|
||||
var gotConn map[string]string
|
||||
gohcl2.DecodeBody(p.ConnInfo.Body, nil, &gotConn)
|
||||
if !reflect.DeepEqual(gotConn, wantConn) {
|
||||
t.Errorf("wrong Resources[1].Provisioners[0].ConnInfo.Body %#v; want %#v", gotConn, wantConn)
|
||||
}
|
||||
}
|
||||
|
||||
// We'll use these throwaway structs to more easily decode and
|
||||
// compare the main config body.
|
||||
type instanceNetworkInterface struct {
|
||||
DeviceIndex int `hcl:"device_index"`
|
||||
Description string `hcl:"description"`
|
||||
}
|
||||
type instanceConfig struct {
|
||||
AMI string `hcl:"ami"`
|
||||
SecurityGroups []string `hcl:"security_groups"`
|
||||
NetworkInterface instanceNetworkInterface `hcl:"network_interface,block"`
|
||||
}
|
||||
var gotConfig instanceConfig
|
||||
wantConfig := instanceConfig{
|
||||
AMI: "ami-abc123",
|
||||
SecurityGroups: []string{"foo", "sg-firewall"},
|
||||
NetworkInterface: instanceNetworkInterface{
|
||||
DeviceIndex: 0,
|
||||
Description: "Main network interface",
|
||||
},
|
||||
}
|
||||
ctx := &hcl2.EvalContext{
|
||||
Variables: map[string]cty.Value{
|
||||
"var": cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.StringVal("ami-abc123"),
|
||||
}),
|
||||
"aws_security_group": cty.ObjectVal(map[string]cty.Value{
|
||||
"firewall": cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.StringVal("sg-firewall"),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
}
|
||||
diags := gohcl2.DecodeBody(r.RawConfig.Body, ctx, &gotConfig)
|
||||
if len(diags) != 0 {
|
||||
t.Errorf("unexpected diagnostics decoding Resources[1].RawConfig.Body")
|
||||
for _, diag := range diags {
|
||||
t.Logf("- %s", diag.Error())
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(gotConfig, wantConfig) {
|
||||
t.Errorf("wrong Resources[1].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
|
||||
}
|
||||
|
||||
}
|
||||
{
|
||||
r := cfg.Resources[2]
|
||||
|
||||
if got, want := r.Id(), "aws_instance.db"; got != want {
|
||||
t.Errorf("wrong Resources[2].Id() %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := r.DependsOn, []string{"aws_instance.web"}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong Resources[2].DependsOn %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
if got, want := len(r.Provisioners), 1; got != want {
|
||||
t.Errorf("wrong Resources[2].Provisioners length %#v; want %#v", got, want)
|
||||
} else {
|
||||
p := r.Provisioners[0]
|
||||
|
||||
if got, want := p.Type, "file"; got != want {
|
||||
t.Errorf("wrong Resources[2].Provisioners[0].Type %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
wantConfig := map[string]string{
|
||||
"source": "here",
|
||||
"destination": "there",
|
||||
}
|
||||
var gotConfig map[string]string
|
||||
gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig)
|
||||
if !reflect.DeepEqual(gotConfig, wantConfig) {
|
||||
t.Errorf("wrong Resources[2].Provisioners[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
|
||||
}
|
||||
|
||||
wantConn := map[string]string{
|
||||
"default": "false",
|
||||
}
|
||||
var gotConn map[string]string
|
||||
gohcl2.DecodeBody(p.ConnInfo.Body, nil, &gotConn)
|
||||
if !reflect.DeepEqual(gotConn, wantConn) {
|
||||
t.Errorf("wrong Resources[2].Provisioners[0].ConnInfo.Body %#v; want %#v", gotConn, wantConn)
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
r := cfg.Resources[3]
|
||||
|
||||
if got, want := r.Id(), "data.do.simple"; got != want {
|
||||
t.Errorf("wrong Resources[3].Id() %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := r.DependsOn, []string(nil); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong Resources[3].DependsOn %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := r.Provider, "do.foo"; got != want {
|
||||
t.Errorf("wrong Resources[3].Provider %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
wantConfig := map[string]string{
|
||||
"foo": "baz",
|
||||
}
|
||||
var gotConfig map[string]string
|
||||
gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig)
|
||||
if !reflect.DeepEqual(gotConfig, wantConfig) {
|
||||
t.Errorf("wrong Resources[3].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
|
||||
}
|
||||
}
|
||||
{
|
||||
r := cfg.Resources[4]
|
||||
|
||||
if got, want := r.Id(), "data.do.depends"; got != want {
|
||||
t.Errorf("wrong Resources[4].Id() %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := r.DependsOn, []string{"data.do.simple"}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong Resources[4].DependsOn %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := r.Provider, ""; got != want {
|
||||
t.Errorf("wrong Resources[4].Provider %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
wantConfig := map[string]string{}
|
||||
var gotConfig map[string]string
|
||||
gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig)
|
||||
if !reflect.DeepEqual(gotConfig, wantConfig) {
|
||||
t.Errorf("wrong Resources[4].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "variable" blocks
|
||||
if got, want := len(cfg.Variables), 3; got != want {
|
||||
t.Errorf("Variables slice has wrong length %#v; want %#v", got, want)
|
||||
} else {
|
||||
{
|
||||
v := cfg.Variables[0]
|
||||
|
||||
if got, want := v.Name, "foo"; got != want {
|
||||
t.Errorf("wrong Variables[0].Name %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := v.Default, "bar"; got != want {
|
||||
t.Errorf("wrong Variables[0].Default %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := v.Description, "barbar"; got != want {
|
||||
t.Errorf("wrong Variables[0].Description %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := v.DeclaredType, ""; got != want {
|
||||
t.Errorf("wrong Variables[0].DeclaredType %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
{
|
||||
v := cfg.Variables[1]
|
||||
|
||||
if got, want := v.Name, "bar"; got != want {
|
||||
t.Errorf("wrong Variables[1].Name %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := v.Default, interface{}(nil); got != want {
|
||||
t.Errorf("wrong Variables[1].Default %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := v.Description, ""; got != want {
|
||||
t.Errorf("wrong Variables[1].Description %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := v.DeclaredType, "string"; got != want {
|
||||
t.Errorf("wrong Variables[1].DeclaredType %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
{
|
||||
v := cfg.Variables[2]
|
||||
|
||||
if got, want := v.Name, "baz"; got != want {
|
||||
t.Errorf("wrong Variables[2].Name %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := v.Default, map[string]interface{}{"key": "value"}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong Variables[2].Default %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := v.Description, ""; got != want {
|
||||
t.Errorf("wrong Variables[2].Description %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := v.DeclaredType, "map"; got != want {
|
||||
t.Errorf("wrong Variables[2].DeclaredType %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "output" blocks
|
||||
if got, want := len(cfg.Outputs), 2; got != want {
|
||||
t.Errorf("Outputs slice has wrong length %#v; want %#v", got, want)
|
||||
} else {
|
||||
{
|
||||
o := cfg.Outputs[0]
|
||||
|
||||
if got, want := o.Name, "web_ip"; got != want {
|
||||
t.Errorf("wrong Outputs[0].Name %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := o.DependsOn, []string(nil); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong Outputs[0].DependsOn %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := o.Description, ""; got != want {
|
||||
t.Errorf("wrong Outputs[0].Description %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := o.Sensitive, true; got != want {
|
||||
t.Errorf("wrong Outputs[0].Sensitive %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
wantConfig := map[string]string{
|
||||
"value": "312.213.645.123",
|
||||
}
|
||||
var gotConfig map[string]string
|
||||
ctx := &hcl2.EvalContext{
|
||||
Variables: map[string]cty.Value{
|
||||
"aws_instance": cty.ObjectVal(map[string]cty.Value{
|
||||
"web": cty.ObjectVal(map[string]cty.Value{
|
||||
"private_ip": cty.StringVal("312.213.645.123"),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
}
|
||||
gohcl2.DecodeBody(o.RawConfig.Body, ctx, &gotConfig)
|
||||
if !reflect.DeepEqual(gotConfig, wantConfig) {
|
||||
t.Errorf("wrong Outputs[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
|
||||
}
|
||||
}
|
||||
{
|
||||
o := cfg.Outputs[1]
|
||||
|
||||
if got, want := o.Name, "web_id"; got != want {
|
||||
t.Errorf("wrong Outputs[1].Name %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := o.DependsOn, []string{"aws_instance.db"}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("wrong Outputs[1].DependsOn %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := o.Description, "The ID"; got != want {
|
||||
t.Errorf("wrong Outputs[1].Description %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := o.Sensitive, false; got != want {
|
||||
t.Errorf("wrong Outputs[1].Sensitive %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "provider" blocks
|
||||
if got, want := len(cfg.ProviderConfigs), 2; got != want {
|
||||
t.Errorf("ProviderConfigs slice has wrong length %#v; want %#v", got, want)
|
||||
} else {
|
||||
{
|
||||
p := cfg.ProviderConfigs[0]
|
||||
|
||||
if got, want := p.Name, "aws"; got != want {
|
||||
t.Errorf("wrong ProviderConfigs[0].Name %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := p.Alias, ""; got != want {
|
||||
t.Errorf("wrong ProviderConfigs[0].Alias %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := p.Version, "1.0.0"; got != want {
|
||||
t.Errorf("wrong ProviderConfigs[0].Version %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
wantConfig := map[string]string{
|
||||
"access_key": "foo",
|
||||
"secret_key": "bar",
|
||||
}
|
||||
var gotConfig map[string]string
|
||||
gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig)
|
||||
if !reflect.DeepEqual(gotConfig, wantConfig) {
|
||||
t.Errorf("wrong ProviderConfigs[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
|
||||
}
|
||||
|
||||
}
|
||||
{
|
||||
p := cfg.ProviderConfigs[1]
|
||||
|
||||
if got, want := p.Name, "do"; got != want {
|
||||
t.Errorf("wrong ProviderConfigs[1].Name %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := p.Alias, "fum"; got != want {
|
||||
t.Errorf("wrong ProviderConfigs[1].Alias %#v; want %#v", got, want)
|
||||
}
|
||||
if got, want := p.Version, ""; got != want {
|
||||
t.Errorf("wrong ProviderConfigs[1].Version %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// "locals" definitions
|
||||
if got, want := len(cfg.Locals), 5; got != want {
|
||||
t.Errorf("Locals slice has wrong length %#v; want %#v", got, want)
|
||||
} else {
|
||||
{
|
||||
l := cfg.Locals[0]
|
||||
|
||||
if got, want := l.Name, "security_group_ids"; got != want {
|
||||
t.Errorf("wrong Locals[0].Name %#v; want %#v", got, want)
|
||||
}
|
||||
|
||||
wantConfig := map[string][]string{
|
||||
"value": []string{"sg-abc123"},
|
||||
}
|
||||
var gotConfig map[string][]string
|
||||
ctx := &hcl2.EvalContext{
|
||||
Variables: map[string]cty.Value{
|
||||
"aws_security_group": cty.ObjectVal(map[string]cty.Value{
|
||||
"firewall": cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("sg-abc123"),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
}
|
||||
gohcl2.DecodeBody(l.RawConfig.Body, ctx, &gotConfig)
|
||||
if !reflect.DeepEqual(gotConfig, wantConfig) {
|
||||
t.Errorf("wrong Locals[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig)
|
||||
}
|
||||
}
|
||||
{
|
||||
l := cfg.Locals[1]
|
||||
|
||||
if got, want := l.Name, "web_ip"; got != want {
|
||||
t.Errorf("wrong Locals[1].Name %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
{
|
||||
l := cfg.Locals[2]
|
||||
|
||||
if got, want := l.Name, "literal"; got != want {
|
||||
t.Errorf("wrong Locals[2].Name %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
{
|
||||
l := cfg.Locals[3]
|
||||
|
||||
if got, want := l.Name, "literal_list"; got != want {
|
||||
t.Errorf("wrong Locals[3].Name %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
{
|
||||
l := cfg.Locals[4]
|
||||
|
||||
if got, want := l.Name, "literal_map"; got != want {
|
||||
t.Errorf("wrong Locals[4].Name %#v; want %#v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHCLConfigurableConfigurable(t *testing.T) {
|
||||
var _ configurable = new(hclConfigurable)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
204
config/merge.go
204
config/merge.go
@ -1,204 +0,0 @@
|
||||
package config
|
||||
|
||||
// Merge merges two configurations into a single configuration.
|
||||
//
|
||||
// Merge allows for the two configurations to have duplicate resources,
|
||||
// because the resources will be merged. This differs from a single
|
||||
// Config which must only have unique resources.
|
||||
func Merge(c1, c2 *Config) (*Config, error) {
|
||||
c := new(Config)
|
||||
|
||||
// Merge unknown keys
|
||||
unknowns := make(map[string]struct{})
|
||||
for _, k := range c1.unknownKeys {
|
||||
_, present := unknowns[k]
|
||||
if !present {
|
||||
unknowns[k] = struct{}{}
|
||||
c.unknownKeys = append(c.unknownKeys, k)
|
||||
}
|
||||
}
|
||||
for _, k := range c2.unknownKeys {
|
||||
_, present := unknowns[k]
|
||||
if !present {
|
||||
unknowns[k] = struct{}{}
|
||||
c.unknownKeys = append(c.unknownKeys, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge Atlas configuration. This is a dumb one overrides the other
|
||||
// sort of merge.
|
||||
c.Atlas = c1.Atlas
|
||||
if c2.Atlas != nil {
|
||||
c.Atlas = c2.Atlas
|
||||
}
|
||||
|
||||
// Merge the Terraform configuration
|
||||
if c1.Terraform != nil {
|
||||
c.Terraform = c1.Terraform
|
||||
if c2.Terraform != nil {
|
||||
c.Terraform.Merge(c2.Terraform)
|
||||
}
|
||||
} else {
|
||||
c.Terraform = c2.Terraform
|
||||
}
|
||||
|
||||
// NOTE: Everything below is pretty gross. Due to the lack of generics
|
||||
// in Go, there is some hoop-jumping involved to make this merging a
|
||||
// little more test-friendly and less repetitive. Ironically, making it
|
||||
// less repetitive involves being a little repetitive, but I prefer to
|
||||
// be repetitive with things that are less error prone than things that
|
||||
// are more error prone (more logic). Type conversions to an interface
|
||||
// are pretty low-error.
|
||||
|
||||
var m1, m2, mresult []merger
|
||||
|
||||
// Modules
|
||||
m1 = make([]merger, 0, len(c1.Modules))
|
||||
m2 = make([]merger, 0, len(c2.Modules))
|
||||
for _, v := range c1.Modules {
|
||||
m1 = append(m1, v)
|
||||
}
|
||||
for _, v := range c2.Modules {
|
||||
m2 = append(m2, v)
|
||||
}
|
||||
mresult = mergeSlice(m1, m2)
|
||||
if len(mresult) > 0 {
|
||||
c.Modules = make([]*Module, len(mresult))
|
||||
for i, v := range mresult {
|
||||
c.Modules[i] = v.(*Module)
|
||||
}
|
||||
}
|
||||
|
||||
// Outputs
|
||||
m1 = make([]merger, 0, len(c1.Outputs))
|
||||
m2 = make([]merger, 0, len(c2.Outputs))
|
||||
for _, v := range c1.Outputs {
|
||||
m1 = append(m1, v)
|
||||
}
|
||||
for _, v := range c2.Outputs {
|
||||
m2 = append(m2, v)
|
||||
}
|
||||
mresult = mergeSlice(m1, m2)
|
||||
if len(mresult) > 0 {
|
||||
c.Outputs = make([]*Output, len(mresult))
|
||||
for i, v := range mresult {
|
||||
c.Outputs[i] = v.(*Output)
|
||||
}
|
||||
}
|
||||
|
||||
// Provider Configs
|
||||
m1 = make([]merger, 0, len(c1.ProviderConfigs))
|
||||
m2 = make([]merger, 0, len(c2.ProviderConfigs))
|
||||
for _, v := range c1.ProviderConfigs {
|
||||
m1 = append(m1, v)
|
||||
}
|
||||
for _, v := range c2.ProviderConfigs {
|
||||
m2 = append(m2, v)
|
||||
}
|
||||
mresult = mergeSlice(m1, m2)
|
||||
if len(mresult) > 0 {
|
||||
c.ProviderConfigs = make([]*ProviderConfig, len(mresult))
|
||||
for i, v := range mresult {
|
||||
c.ProviderConfigs[i] = v.(*ProviderConfig)
|
||||
}
|
||||
}
|
||||
|
||||
// Resources
|
||||
m1 = make([]merger, 0, len(c1.Resources))
|
||||
m2 = make([]merger, 0, len(c2.Resources))
|
||||
for _, v := range c1.Resources {
|
||||
m1 = append(m1, v)
|
||||
}
|
||||
for _, v := range c2.Resources {
|
||||
m2 = append(m2, v)
|
||||
}
|
||||
mresult = mergeSlice(m1, m2)
|
||||
if len(mresult) > 0 {
|
||||
c.Resources = make([]*Resource, len(mresult))
|
||||
for i, v := range mresult {
|
||||
c.Resources[i] = v.(*Resource)
|
||||
}
|
||||
}
|
||||
|
||||
// Variables
|
||||
m1 = make([]merger, 0, len(c1.Variables))
|
||||
m2 = make([]merger, 0, len(c2.Variables))
|
||||
for _, v := range c1.Variables {
|
||||
m1 = append(m1, v)
|
||||
}
|
||||
for _, v := range c2.Variables {
|
||||
m2 = append(m2, v)
|
||||
}
|
||||
mresult = mergeSlice(m1, m2)
|
||||
if len(mresult) > 0 {
|
||||
c.Variables = make([]*Variable, len(mresult))
|
||||
for i, v := range mresult {
|
||||
c.Variables[i] = v.(*Variable)
|
||||
}
|
||||
}
|
||||
|
||||
// Local Values
|
||||
// These are simpler than the other config elements because they are just
|
||||
// flat values and so no deep merging is required.
|
||||
if localsCount := len(c1.Locals) + len(c2.Locals); localsCount != 0 {
|
||||
// Explicit length check above because we want c.Locals to remain
|
||||
// nil if the result would be empty.
|
||||
c.Locals = make([]*Local, 0, len(c1.Locals)+len(c2.Locals))
|
||||
c.Locals = append(c.Locals, c1.Locals...)
|
||||
c.Locals = append(c.Locals, c2.Locals...)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// merger is an interface that must be implemented by types that are
|
||||
// merge-able. This simplifies the implementation of Merge for the various
|
||||
// components of a Config.
|
||||
type merger interface {
|
||||
mergerName() string
|
||||
mergerMerge(merger) merger
|
||||
}
|
||||
|
||||
// mergeSlice merges a slice of mergers.
|
||||
func mergeSlice(m1, m2 []merger) []merger {
|
||||
r := make([]merger, len(m1), len(m1)+len(m2))
|
||||
copy(r, m1)
|
||||
|
||||
m := map[string]struct{}{}
|
||||
for _, v2 := range m2 {
|
||||
// If we already saw it, just append it because its a
|
||||
// duplicate and invalid...
|
||||
name := v2.mergerName()
|
||||
if _, ok := m[name]; ok {
|
||||
r = append(r, v2)
|
||||
continue
|
||||
}
|
||||
m[name] = struct{}{}
|
||||
|
||||
// Find an original to override
|
||||
var original merger
|
||||
originalIndex := -1
|
||||
for i, v := range m1 {
|
||||
if v.mergerName() == name {
|
||||
originalIndex = i
|
||||
original = v
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var v merger
|
||||
if original == nil {
|
||||
v = v2
|
||||
} else {
|
||||
v = original.mergerMerge(v2)
|
||||
}
|
||||
|
||||
if originalIndex == -1 {
|
||||
r = append(r, v)
|
||||
} else {
|
||||
r[originalIndex] = v
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
@ -1,499 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
cases := []struct {
|
||||
c1, c2, result *Config
|
||||
err bool
|
||||
}{
|
||||
// Normal good case.
|
||||
{
|
||||
&Config{
|
||||
Atlas: &AtlasConfig{
|
||||
Name: "foo",
|
||||
},
|
||||
Modules: []*Module{
|
||||
&Module{Name: "foo"},
|
||||
},
|
||||
Outputs: []*Output{
|
||||
&Output{Name: "foo"},
|
||||
},
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Name: "foo"},
|
||||
},
|
||||
Resources: []*Resource{
|
||||
&Resource{Name: "foo"},
|
||||
},
|
||||
Variables: []*Variable{
|
||||
&Variable{Name: "foo"},
|
||||
},
|
||||
Locals: []*Local{
|
||||
&Local{Name: "foo"},
|
||||
},
|
||||
|
||||
unknownKeys: []string{"foo"},
|
||||
},
|
||||
|
||||
&Config{
|
||||
Atlas: &AtlasConfig{
|
||||
Name: "bar",
|
||||
},
|
||||
Modules: []*Module{
|
||||
&Module{Name: "bar"},
|
||||
},
|
||||
Outputs: []*Output{
|
||||
&Output{Name: "bar"},
|
||||
},
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Name: "bar"},
|
||||
},
|
||||
Resources: []*Resource{
|
||||
&Resource{Name: "bar"},
|
||||
},
|
||||
Variables: []*Variable{
|
||||
&Variable{Name: "bar"},
|
||||
},
|
||||
Locals: []*Local{
|
||||
&Local{Name: "bar"},
|
||||
},
|
||||
|
||||
unknownKeys: []string{"bar"},
|
||||
},
|
||||
|
||||
&Config{
|
||||
Atlas: &AtlasConfig{
|
||||
Name: "bar",
|
||||
},
|
||||
Modules: []*Module{
|
||||
&Module{Name: "foo"},
|
||||
&Module{Name: "bar"},
|
||||
},
|
||||
Outputs: []*Output{
|
||||
&Output{Name: "foo"},
|
||||
&Output{Name: "bar"},
|
||||
},
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Name: "foo"},
|
||||
&ProviderConfig{Name: "bar"},
|
||||
},
|
||||
Resources: []*Resource{
|
||||
&Resource{Name: "foo"},
|
||||
&Resource{Name: "bar"},
|
||||
},
|
||||
Variables: []*Variable{
|
||||
&Variable{Name: "foo"},
|
||||
&Variable{Name: "bar"},
|
||||
},
|
||||
Locals: []*Local{
|
||||
&Local{Name: "foo"},
|
||||
&Local{Name: "bar"},
|
||||
},
|
||||
|
||||
unknownKeys: []string{"foo", "bar"},
|
||||
},
|
||||
|
||||
false,
|
||||
},
|
||||
|
||||
// Test that when merging duplicates, it merges into the
|
||||
// first, but keeps the duplicates so that errors still
|
||||
// happen.
|
||||
{
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Name: "foo"},
|
||||
},
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Name: "foo"},
|
||||
},
|
||||
Resources: []*Resource{
|
||||
&Resource{Name: "foo"},
|
||||
},
|
||||
Variables: []*Variable{
|
||||
&Variable{Name: "foo", Default: "foo"},
|
||||
&Variable{Name: "foo"},
|
||||
},
|
||||
Locals: []*Local{
|
||||
&Local{Name: "foo"},
|
||||
},
|
||||
|
||||
unknownKeys: []string{"foo"},
|
||||
},
|
||||
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Name: "bar"},
|
||||
},
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Name: "bar"},
|
||||
},
|
||||
Resources: []*Resource{
|
||||
&Resource{Name: "bar"},
|
||||
},
|
||||
Variables: []*Variable{
|
||||
&Variable{Name: "foo", Default: "bar"},
|
||||
&Variable{Name: "bar"},
|
||||
},
|
||||
Locals: []*Local{
|
||||
&Local{Name: "foo"},
|
||||
},
|
||||
|
||||
unknownKeys: []string{"bar"},
|
||||
},
|
||||
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Name: "foo"},
|
||||
&Output{Name: "bar"},
|
||||
},
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Name: "foo"},
|
||||
&ProviderConfig{Name: "bar"},
|
||||
},
|
||||
Resources: []*Resource{
|
||||
&Resource{Name: "foo"},
|
||||
&Resource{Name: "bar"},
|
||||
},
|
||||
Variables: []*Variable{
|
||||
&Variable{Name: "foo", Default: "bar"},
|
||||
&Variable{Name: "foo"},
|
||||
&Variable{Name: "bar"},
|
||||
},
|
||||
Locals: []*Local{
|
||||
&Local{Name: "foo"},
|
||||
&Local{Name: "foo"},
|
||||
},
|
||||
|
||||
unknownKeys: []string{"foo", "bar"},
|
||||
},
|
||||
|
||||
false,
|
||||
},
|
||||
|
||||
// Terraform block
|
||||
{
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
&Config{},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
// Provider alias
|
||||
{
|
||||
&Config{
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Alias: "foo"},
|
||||
},
|
||||
},
|
||||
&Config{},
|
||||
&Config{
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Alias: "foo"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{},
|
||||
&Config{
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Alias: "foo"},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Alias: "foo"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Alias: "bar"},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Alias: "foo"},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
ProviderConfigs: []*ProviderConfig{
|
||||
&ProviderConfig{Alias: "foo"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
// Variable type
|
||||
{
|
||||
&Config{
|
||||
Variables: []*Variable{
|
||||
&Variable{DeclaredType: "foo"},
|
||||
},
|
||||
},
|
||||
&Config{},
|
||||
&Config{
|
||||
Variables: []*Variable{
|
||||
&Variable{DeclaredType: "foo"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{},
|
||||
&Config{
|
||||
Variables: []*Variable{
|
||||
&Variable{DeclaredType: "foo"},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Variables: []*Variable{
|
||||
&Variable{DeclaredType: "foo"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{
|
||||
Variables: []*Variable{
|
||||
&Variable{DeclaredType: "bar"},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Variables: []*Variable{
|
||||
&Variable{DeclaredType: "foo"},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Variables: []*Variable{
|
||||
&Variable{DeclaredType: "foo"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
// Output description
|
||||
{
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Description: "foo"},
|
||||
},
|
||||
},
|
||||
&Config{},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Description: "foo"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Description: "foo"},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Description: "foo"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Description: "bar"},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Description: "foo"},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Description: "foo"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
// Output depends_on
|
||||
{
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{DependsOn: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
&Config{},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{DependsOn: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{DependsOn: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{DependsOn: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{DependsOn: []string{"bar"}},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{DependsOn: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{DependsOn: []string{"foo"}},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
// Output sensitive
|
||||
{
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Sensitive: true},
|
||||
},
|
||||
},
|
||||
&Config{},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Sensitive: true},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Sensitive: true},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Sensitive: true},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Sensitive: false},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Sensitive: true},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Outputs: []*Output{
|
||||
&Output{Sensitive: true},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
// terraform blocks are merged, not overwritten
|
||||
{
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
Backend: &Backend{
|
||||
Type: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
&Config{
|
||||
Terraform: &Terraform{
|
||||
RequiredVersion: "A",
|
||||
Backend: &Backend{
|
||||
Type: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
|
||||
actual, err := Merge(tc.c1, tc.c2)
|
||||
if err != nil != tc.err {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, tc.result) {
|
||||
t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(actual), spew.Sdump(tc.result))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package config
|
||||
|
||||
import "github.com/blang/semver"
|
||||
|
||||
// ProviderVersionConstraint presents a constraint for a particular
|
||||
// provider, identified by its full name.
|
||||
type ProviderVersionConstraint struct {
|
||||
Constraint string
|
||||
ProviderType string
|
||||
}
|
||||
|
||||
// ProviderVersionConstraints is a map from provider full name to its associated
|
||||
// ProviderVersionConstraint, as produced by Config.RequiredProviders.
|
||||
type ProviderVersionConstraints map[string]ProviderVersionConstraint
|
||||
|
||||
// RequiredRanges returns a semver.Range for each distinct provider type in
|
||||
// the constraint map. If the same provider type appears more than once
|
||||
// (e.g. because aliases are in use) then their respective constraints are
|
||||
// combined such that they must *all* apply.
|
||||
//
|
||||
// The result of this method can be passed to the
|
||||
// PluginMetaSet.ConstrainVersions method within the plugin/discovery
|
||||
// package in order to filter down the available plugins to those which
|
||||
// satisfy the given constraints.
|
||||
//
|
||||
// This function will panic if any of the constraints within cannot be
|
||||
// parsed as semver ranges. This is guaranteed to never happen for a
|
||||
// constraint set that was built from a configuration that passed validation.
|
||||
func (cons ProviderVersionConstraints) RequiredRanges() map[string]semver.Range {
|
||||
ret := make(map[string]semver.Range, len(cons))
|
||||
|
||||
for _, con := range cons {
|
||||
spec := semver.MustParseRange(con.Constraint)
|
||||
if existing, exists := ret[con.ProviderType]; exists {
|
||||
ret[con.ProviderType] = existing.AND(spec)
|
||||
} else {
|
||||
ret[con.ProviderType] = spec
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// ProviderConfigsByFullName returns a map from provider full names (as
|
||||
// returned by ProviderConfig.FullName()) to the corresponding provider
|
||||
// configs.
|
||||
//
|
||||
// This function returns no new information than what's already in
|
||||
// c.ProviderConfigs, but returns it in a more convenient shape. If there
|
||||
// is more than one provider config with the same full name then the result
|
||||
// is undefined, but that is guaranteed not to happen for any config that
|
||||
// has passed validation.
|
||||
func (c *Config) ProviderConfigsByFullName() map[string]*ProviderConfig {
|
||||
ret := make(map[string]*ProviderConfig, len(c.ProviderConfigs))
|
||||
|
||||
for _, pc := range c.ProviderConfigs {
|
||||
ret[pc.FullName()] = pc
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package config
|
||||
|
||||
// ProvisionerWhen is an enum for valid values for when to run provisioners.
|
||||
type ProvisionerWhen int
|
||||
|
||||
const (
|
||||
ProvisionerWhenInvalid ProvisionerWhen = iota
|
||||
ProvisionerWhenCreate
|
||||
ProvisionerWhenDestroy
|
||||
)
|
||||
|
||||
var provisionerWhenStrs = map[ProvisionerWhen]string{
|
||||
ProvisionerWhenInvalid: "invalid",
|
||||
ProvisionerWhenCreate: "create",
|
||||
ProvisionerWhenDestroy: "destroy",
|
||||
}
|
||||
|
||||
func (v ProvisionerWhen) String() string {
|
||||
return provisionerWhenStrs[v]
|
||||
}
|
||||
|
||||
// ProvisionerOnFailure is an enum for valid values for on_failure options
|
||||
// for provisioners.
|
||||
type ProvisionerOnFailure int
|
||||
|
||||
const (
|
||||
ProvisionerOnFailureInvalid ProvisionerOnFailure = iota
|
||||
ProvisionerOnFailureContinue
|
||||
ProvisionerOnFailureFail
|
||||
)
|
||||
|
||||
var provisionerOnFailureStrs = map[ProvisionerOnFailure]string{
|
||||
ProvisionerOnFailureInvalid: "invalid",
|
||||
ProvisionerOnFailureContinue: "continue",
|
||||
ProvisionerOnFailureFail: "fail",
|
||||
}
|
||||
|
||||
func (v ProvisionerOnFailure) String() string {
|
||||
return provisionerOnFailureStrs[v]
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package config
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go
|
||||
type ResourceMode int
|
||||
|
||||
const (
|
||||
ManagedResourceMode ResourceMode = iota
|
||||
DataResourceMode
|
||||
)
|
@ -1,24 +0,0 @@
|
||||
// Code generated by "stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go"; DO NOT EDIT.
|
||||
|
||||
package config
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ManagedResourceMode-0]
|
||||
_ = x[DataResourceMode-1]
|
||||
}
|
||||
|
||||
const _ResourceMode_name = "ManagedResourceModeDataResourceMode"
|
||||
|
||||
var _ResourceMode_index = [...]uint8{0, 19, 35}
|
||||
|
||||
func (i ResourceMode) String() string {
|
||||
if i < 0 || i >= ResourceMode(len(_ResourceMode_index)-1) {
|
||||
return "ResourceMode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _ResourceMode_name[_ResourceMode_index[i]:_ResourceMode_index[i+1]]
|
||||
}
|
1
config/testdata/.gitattributes
vendored
1
config/testdata/.gitattributes
vendored
@ -1 +0,0 @@
|
||||
windows-line-endings.tf eol=crlf
|
15
config/testdata/attributes.tf
vendored
15
config/testdata/attributes.tf
vendored
@ -1,15 +0,0 @@
|
||||
provider "cloudstack" {
|
||||
api_url = "bla"
|
||||
api_key = "bla"
|
||||
secret_key = "bla"
|
||||
}
|
||||
|
||||
resource "cloudstack_firewall" "test" {
|
||||
ipaddress = "192.168.0.1"
|
||||
|
||||
rule {
|
||||
source_cidr = "10.0.0.0/8"
|
||||
protocol = "tcp"
|
||||
ports = ["80", "1000-2000"]
|
||||
}
|
||||
}
|
27
config/testdata/attributes.tf.json
vendored
27
config/testdata/attributes.tf.json
vendored
@ -1,27 +0,0 @@
|
||||
{
|
||||
"provider": {
|
||||
"cloudstack": {
|
||||
"api_url": "bla",
|
||||
"api_key": "bla",
|
||||
"secret_key": "bla"
|
||||
}
|
||||
},
|
||||
"resource": {
|
||||
"cloudstack_firewall": {
|
||||
"test": {
|
||||
"ipaddress": "192.168.0.1",
|
||||
"rule": [
|
||||
{
|
||||
"source_cidr": "10.0.0.0/8",
|
||||
"protocol": "tcp",
|
||||
"ports": [
|
||||
"80",
|
||||
"1000-2000"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
7
config/testdata/backend-hash-basic/main.tf
vendored
7
config/testdata/backend-hash-basic/main.tf
vendored
@ -1,7 +0,0 @@
|
||||
terraform {
|
||||
backend "foo" {
|
||||
foo = "bar"
|
||||
bar = ["baz"]
|
||||
map = { a = "b" }
|
||||
}
|
||||
}
|
1
config/testdata/backend-hash-empty/main.tf
vendored
1
config/testdata/backend-hash-empty/main.tf
vendored
@ -1 +0,0 @@
|
||||
terraform {}
|
@ -1 +0,0 @@
|
||||
# Empty
|
@ -1,4 +0,0 @@
|
||||
terraform {
|
||||
backend "foo" {
|
||||
}
|
||||
}
|
3
config/testdata/bad-variable-type.tf
vendored
3
config/testdata/bad-variable-type.tf
vendored
@ -1,3 +0,0 @@
|
||||
variable "bad_type" {
|
||||
type = "notatype"
|
||||
}
|
1
config/testdata/bad_type.tf.nope
vendored
1
config/testdata/bad_type.tf.nope
vendored
@ -1 +0,0 @@
|
||||
variable "foo" {}
|
125
config/testdata/basic-hcl2.tf
vendored
125
config/testdata/basic-hcl2.tf
vendored
@ -1,125 +0,0 @@
|
||||
#terraform:hcl2
|
||||
|
||||
terraform {
|
||||
required_version = "foo"
|
||||
|
||||
backend "baz" {
|
||||
something = "nothing"
|
||||
}
|
||||
}
|
||||
|
||||
variable "foo" {
|
||||
default = "bar"
|
||||
description = "barbar"
|
||||
}
|
||||
|
||||
variable "bar" {
|
||||
type = "string"
|
||||
}
|
||||
|
||||
variable "baz" {
|
||||
type = "map"
|
||||
|
||||
default = {
|
||||
key = "value"
|
||||
}
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
access_key = "foo"
|
||||
secret_key = "bar"
|
||||
version = "1.0.0"
|
||||
}
|
||||
|
||||
provider "do" {
|
||||
api_key = var.foo
|
||||
alias = "fum"
|
||||
}
|
||||
|
||||
data "do" "simple" {
|
||||
foo = "baz"
|
||||
provider = "do.foo"
|
||||
}
|
||||
|
||||
data "do" "depends" {
|
||||
depends_on = ["data.do.simple"]
|
||||
}
|
||||
|
||||
resource "aws_security_group" "firewall" {
|
||||
count = 5
|
||||
provider = "another"
|
||||
}
|
||||
|
||||
resource "aws_instance" "web" {
|
||||
ami = "${var.foo}"
|
||||
security_groups = [
|
||||
"foo",
|
||||
aws_security_group.firewall.foo,
|
||||
]
|
||||
|
||||
network_interface {
|
||||
device_index = 0
|
||||
description = "Main network interface"
|
||||
}
|
||||
|
||||
connection {
|
||||
default = true
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
source = "foo"
|
||||
destination = "bar"
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
security_group_ids = aws_security_group.firewall.*.id
|
||||
web_ip = aws_instance.web.private_ip
|
||||
}
|
||||
|
||||
locals {
|
||||
literal = 2
|
||||
literal_list = ["foo"]
|
||||
literal_map = {"foo" = "bar"}
|
||||
}
|
||||
|
||||
resource "aws_instance" "db" {
|
||||
security_groups = aws_security_group.firewall.*.id
|
||||
VPC = "foo"
|
||||
|
||||
tags = {
|
||||
Name = "${var.bar}-database"
|
||||
}
|
||||
|
||||
depends_on = ["aws_instance.web"]
|
||||
|
||||
provisioner "file" {
|
||||
source = "here"
|
||||
destination = "there"
|
||||
|
||||
connection {
|
||||
default = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output "web_ip" {
|
||||
value = aws_instance.web.private_ip
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "web_id" {
|
||||
description = "The ID"
|
||||
value = aws_instance.web.id
|
||||
depends_on = ["aws_instance.db"]
|
||||
}
|
||||
|
||||
atlas {
|
||||
name = "example/foo"
|
||||
}
|
||||
|
||||
module "child" {
|
||||
source = "./baz"
|
||||
|
||||
toasty = true
|
||||
}
|
95
config/testdata/basic.tf
vendored
95
config/testdata/basic.tf
vendored
@ -1,95 +0,0 @@
|
||||
terraform {
|
||||
required_version = "foo"
|
||||
}
|
||||
|
||||
variable "foo" {
|
||||
default = "bar"
|
||||
description = "bar"
|
||||
}
|
||||
|
||||
variable "bar" {
|
||||
type = "string"
|
||||
}
|
||||
|
||||
variable "baz" {
|
||||
type = "map"
|
||||
|
||||
default = {
|
||||
key = "value"
|
||||
}
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
access_key = "foo"
|
||||
secret_key = "bar"
|
||||
}
|
||||
|
||||
provider "do" {
|
||||
api_key = "${var.foo}"
|
||||
}
|
||||
|
||||
data "do" "simple" {
|
||||
foo = "baz"
|
||||
}
|
||||
|
||||
data "do" "depends" {
|
||||
depends_on = ["data.do.simple"]
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
source = "foo"
|
||||
destination = "bar"
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
security_group_ids = "${aws_security_group.firewall.*.id}"
|
||||
web_ip = "${aws_instance.web.private_ip}"
|
||||
}
|
||||
|
||||
locals {
|
||||
literal = 2
|
||||
literal_list = ["foo"]
|
||||
literal_map = {"foo" = "bar"}
|
||||
}
|
||||
|
||||
resource "aws_instance" "db" {
|
||||
security_groups = "${aws_security_group.firewall.*.id}"
|
||||
VPC = "foo"
|
||||
|
||||
depends_on = ["aws_instance.web"]
|
||||
|
||||
provisioner "file" {
|
||||
source = "foo"
|
||||
destination = "bar"
|
||||
}
|
||||
}
|
||||
|
||||
output "web_ip" {
|
||||
value = "${aws_instance.web.private_ip}"
|
||||
}
|
||||
|
||||
output "web_id" {
|
||||
description = "The ID"
|
||||
value = "${aws_instance.web.id}"
|
||||
}
|
||||
|
||||
atlas {
|
||||
name = "mitchellh/foo"
|
||||
}
|
103
config/testdata/basic.tf.json
vendored
103
config/testdata/basic.tf.json
vendored
@ -1,103 +0,0 @@
|
||||
{
|
||||
"variable": {
|
||||
"foo": {
|
||||
"default": "bar",
|
||||
"description": "bar"
|
||||
},
|
||||
"bar": {
|
||||
"type": "string"
|
||||
},
|
||||
"baz": {
|
||||
"type": "map",
|
||||
"default": {
|
||||
"key": "value"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"provider": {
|
||||
"aws": {
|
||||
"access_key": "foo",
|
||||
"secret_key": "bar"
|
||||
},
|
||||
|
||||
"do": {
|
||||
"api_key": "${var.foo}"
|
||||
}
|
||||
},
|
||||
|
||||
"data": {
|
||||
"do": {
|
||||
"simple": {
|
||||
"foo": "baz"
|
||||
},
|
||||
"depends": {
|
||||
"depends_on": ["data.do.simple"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"resource": {
|
||||
"aws_instance": {
|
||||
"db": {
|
||||
"security_groups": ["${aws_security_group.firewall.*.id}"],
|
||||
"VPC": "foo",
|
||||
"depends_on": ["aws_instance.web"],
|
||||
|
||||
"provisioner": [{
|
||||
"file": {
|
||||
"source": "foo",
|
||||
"destination": "bar"
|
||||
}
|
||||
}]
|
||||
},
|
||||
|
||||
"web": {
|
||||
"ami": "${var.foo}",
|
||||
"security_groups": [
|
||||
"foo",
|
||||
"${aws_security_group.firewall.foo}"
|
||||
],
|
||||
"network_interface": {
|
||||
"device_index": 0,
|
||||
"description": "Main network interface"
|
||||
},
|
||||
|
||||
"provisioner": {
|
||||
"file": {
|
||||
"source": "foo",
|
||||
"destination": "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"aws_security_group": {
|
||||
"firewall": {
|
||||
"count": 5
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"locals": {
|
||||
"security_group_ids": "${aws_security_group.firewall.*.id}",
|
||||
"web_ip": "${aws_instance.web.private_ip}",
|
||||
"literal": 2,
|
||||
"literal_list": ["foo"],
|
||||
"literal_map": {"foo": "bar"}
|
||||
},
|
||||
|
||||
"output": {
|
||||
"web_id": {
|
||||
"description": "The ID",
|
||||
"value": "${aws_instance.web.id}"
|
||||
},
|
||||
"web_ip": {
|
||||
"value": "${aws_instance.web.private_ip}"
|
||||
}
|
||||
},
|
||||
|
||||
"atlas": {
|
||||
"name": "mitchellh/foo"
|
||||
}
|
||||
}
|
23
config/testdata/connection.tf
vendored
23
config/testdata/connection.tf
vendored
@ -1,23 +0,0 @@
|
||||
resource "aws_instance" "web" {
|
||||
ami = "${var.foo}"
|
||||
security_groups = [
|
||||
"foo",
|
||||
"${aws_security_group.firewall.foo}"
|
||||
]
|
||||
|
||||
connection {
|
||||
type = "ssh"
|
||||
user = "root"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
path = "foo"
|
||||
connection {
|
||||
user = "nobody"
|
||||
}
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
path = "bar"
|
||||
}
|
||||
}
|
19
config/testdata/copy-basic/main.tf
vendored
19
config/testdata/copy-basic/main.tf
vendored
@ -1,19 +0,0 @@
|
||||
variable "ref" {
|
||||
default = "foo"
|
||||
}
|
||||
|
||||
resource "foo" "bar" {
|
||||
depends_on = ["dep"]
|
||||
provider = "foo-west"
|
||||
count = 2
|
||||
attr = "value"
|
||||
ref = "${var.ref}"
|
||||
|
||||
provisioner "shell" {
|
||||
inline = "echo"
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = ["config"]
|
||||
}
|
||||
}
|
3
config/testdata/count-int/main.tf
vendored
3
config/testdata/count-int/main.tf
vendored
@ -1,3 +0,0 @@
|
||||
resource "foo" "bar" {
|
||||
count = 5
|
||||
}
|
3
config/testdata/count-list/main.tf
vendored
3
config/testdata/count-list/main.tf
vendored
@ -1,3 +0,0 @@
|
||||
resource "foo" "bar" {
|
||||
count = "${var.list}"
|
||||
}
|
3
config/testdata/count-string/main.tf
vendored
3
config/testdata/count-string/main.tf
vendored
@ -1,3 +0,0 @@
|
||||
resource "foo" "bar" {
|
||||
count = "5"
|
||||
}
|
3
config/testdata/count-var/main.tf
vendored
3
config/testdata/count-var/main.tf
vendored
@ -1,3 +0,0 @@
|
||||
resource "foo" "bar" {
|
||||
count = "${var.foo}"
|
||||
}
|
14
config/testdata/create-before-destroy.tf
vendored
14
config/testdata/create-before-destroy.tf
vendored
@ -1,14 +0,0 @@
|
||||
|
||||
resource "aws_instance" "web" {
|
||||
ami = "foo"
|
||||
lifecycle {
|
||||
create_before_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_instance" "bar" {
|
||||
ami = "foo"
|
||||
lifecycle {
|
||||
create_before_destroy = false
|
||||
}
|
||||
}
|
3
config/testdata/data-count/main.tf
vendored
3
config/testdata/data-count/main.tf
vendored
@ -1,3 +0,0 @@
|
||||
data "foo" "bar" {
|
||||
count = 5
|
||||
}
|
3
config/testdata/data-source-arity-mistake.tf
vendored
3
config/testdata/data-source-arity-mistake.tf
vendored
@ -1,3 +0,0 @@
|
||||
# I forgot the data source name!
|
||||
data "null" {
|
||||
}
|
2
config/testdata/dir-basic/README.md
vendored
2
config/testdata/dir-basic/README.md
vendored
@ -1,2 +0,0 @@
|
||||
This file just exists to test that LoadDir doesn't load non-Terraform
|
||||
files.
|
3
config/testdata/dir-basic/nested/nested.tf
vendored
3
config/testdata/dir-basic/nested/nested.tf
vendored
@ -1,3 +0,0 @@
|
||||
output "i-am-nested" {
|
||||
value = "what"
|
||||
}
|
21
config/testdata/dir-basic/one.tf
vendored
21
config/testdata/dir-basic/one.tf
vendored
@ -1,21 +0,0 @@
|
||||
variable "foo" {
|
||||
default = "bar"
|
||||
description = "bar"
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
access_key = "foo"
|
||||
secret_key = "bar"
|
||||
}
|
||||
|
||||
data "do" "simple" {
|
||||
foo = "baz"
|
||||
}
|
||||
|
||||
resource "aws_instance" "db" {
|
||||
security_groups = "${aws_security_group.firewall.*.id}"
|
||||
}
|
||||
|
||||
output "web_ip" {
|
||||
value = "${aws_instance.web.private_ip}"
|
||||
}
|
24
config/testdata/dir-basic/two.tf
vendored
24
config/testdata/dir-basic/two.tf
vendored
@ -1,24 +0,0 @@
|
||||
provider "do" {
|
||||
api_key = "${var.foo}"
|
||||
}
|
||||
|
||||
data "do" "depends" {
|
||||
depends_on = ["data.do.simple"]
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
0
config/testdata/dir-empty/.gitkeep
vendored
0
config/testdata/dir-empty/.gitkeep
vendored
8
config/testdata/dir-merge/one.tf
vendored
8
config/testdata/dir-merge/one.tf
vendored
@ -1,8 +0,0 @@
|
||||
variable "foo" {
|
||||
default = "bar"
|
||||
description = "bar"
|
||||
}
|
||||
|
||||
resource "aws_instance" "db" {
|
||||
security_groups = "${aws_security_group.firewall.*.id}"
|
||||
}
|
2
config/testdata/dir-merge/two.tf
vendored
2
config/testdata/dir-merge/two.tf
vendored
@ -1,2 +0,0 @@
|
||||
resource "aws_instance" "db" {
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
variable "foo" {
|
||||
default = "bar"
|
||||
description = "bar"
|
||||
}
|
4
config/testdata/dir-override-var/main.tf
vendored
4
config/testdata/dir-override-var/main.tf
vendored
@ -1,4 +0,0 @@
|
||||
variable "foo" {
|
||||
default = "bar"
|
||||
description = "bar"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
variable "foo" {
|
||||
default = "baz"
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
{
|
||||
"data": {
|
||||
"do": {
|
||||
"depends": {
|
||||
"hello": "world"
|
||||
}
|
||||
}
|
||||
},
|
||||
"resource": {
|
||||
"aws_instance": {
|
||||
"web": {
|
||||
"foo": "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
config/testdata/dir-override/one.tf
vendored
22
config/testdata/dir-override/one.tf
vendored
@ -1,22 +0,0 @@
|
||||
variable "foo" {
|
||||
default = "bar"
|
||||
description = "bar"
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
access_key = "foo"
|
||||
secret_key = "bar"
|
||||
}
|
||||
|
||||
|
||||
data "do" "simple" {
|
||||
foo = "baz"
|
||||
}
|
||||
|
||||
resource "aws_instance" "db" {
|
||||
security_groups = "${aws_security_group.firewall.*.id}"
|
||||
}
|
||||
|
||||
output "web_ip" {
|
||||
value = "${aws_instance.web.private_ip}"
|
||||
}
|
10
config/testdata/dir-override/override.tf.json
vendored
10
config/testdata/dir-override/override.tf.json
vendored
@ -1,10 +0,0 @@
|
||||
{
|
||||
"resource": {
|
||||
"aws_instance": {
|
||||
"db": {
|
||||
"ami": "foo",
|
||||
"security_groups": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
config/testdata/dir-override/two.tf
vendored
24
config/testdata/dir-override/two.tf
vendored
@ -1,24 +0,0 @@
|
||||
provider "do" {
|
||||
api_key = "${var.foo}"
|
||||
}
|
||||
|
||||
data "do" "depends" {
|
||||
depends_on = ["data.do.simple"]
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
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"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
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}"
|
||||
}
|
17
config/testdata/dir-temporary-files/vim-one.tf~
vendored
17
config/testdata/dir-temporary-files/vim-one.tf~
vendored
@ -1,17 +0,0 @@
|
||||
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}"
|
||||
}
|
11
config/testdata/empty-collections/main.tf
vendored
11
config/testdata/empty-collections/main.tf
vendored
@ -1,11 +0,0 @@
|
||||
variable "empty_string" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "empty_list" {
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "empty_map" {
|
||||
default = {}
|
||||
}
|
0
config/testdata/empty.tf
vendored
0
config/testdata/empty.tf
vendored
7
config/testdata/escapedquotes.tf
vendored
7
config/testdata/escapedquotes.tf
vendored
@ -1,7 +0,0 @@
|
||||
variable "ami" {
|
||||
default = [ "ami", "abc123" ]
|
||||
}
|
||||
|
||||
resource "aws_instance" "quotes" {
|
||||
ami = "${join(\",\", var.ami)}"
|
||||
}
|
BIN
config/testdata/git-crypt.tf
vendored
BIN
config/testdata/git-crypt.tf
vendored
Binary file not shown.
@ -1,5 +0,0 @@
|
||||
{
|
||||
"locals": {
|
||||
"foo": "baz"
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
|
||||
# The use of an equals to assign "locals" is something that would be rejected
|
||||
# by the HCL2 parser (equals is reserved for attributes only) and so we can
|
||||
# use it to verify that the old HCL parser was used.
|
||||
locals {
|
||||
foo = "bar"
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#terraform:hcl2
|
||||
|
||||
locals {
|
||||
# This direct expression is something that would be rejected by the old HCL
|
||||
# parser, so we can use it as a marker that the HCL2 parser was used.
|
||||
foo = 1 + 2
|
||||
}
|
51
config/testdata/heredoc.tf
vendored
51
config/testdata/heredoc.tf
vendored
@ -1,51 +0,0 @@
|
||||
provider "aws" {
|
||||
access_key = "foo"
|
||||
secret_key = "bar"
|
||||
}
|
||||
|
||||
resource "aws_iam_policy" "policy" {
|
||||
name = "test_policy"
|
||||
path = "/"
|
||||
description = "My test policy"
|
||||
policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"ec2:Describe*"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
resource "aws_instance" "test" {
|
||||
ami = "foo"
|
||||
|
||||
provisioner "remote-exec" {
|
||||
inline = [
|
||||
<<EOT
|
||||
sudo \
|
||||
A=val \
|
||||
B=val2 \
|
||||
sh script.sh
|
||||
EOT
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_instance" "heredocwithnumbers" {
|
||||
ami = "foo"
|
||||
|
||||
provisioner "local-exec" {
|
||||
command = <<FOO123
|
||||
echo several
|
||||
lines
|
||||
of output
|
||||
FOO123
|
||||
}
|
||||
}
|
17
config/testdata/ignore-changes.tf
vendored
17
config/testdata/ignore-changes.tf
vendored
@ -1,17 +0,0 @@
|
||||
resource "aws_instance" "web" {
|
||||
ami = "foo"
|
||||
lifecycle {
|
||||
ignore_changes = ["ami"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_instance" "bar" {
|
||||
ami = "foo"
|
||||
lifecycle {
|
||||
ignore_changes = []
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_instance" "baz" {
|
||||
ami = "foo"
|
||||
}
|
10
config/testdata/import.tf
vendored
10
config/testdata/import.tf
vendored
@ -1,10 +0,0 @@
|
||||
variable "foo" {
|
||||
default = "bar"
|
||||
description = "bar"
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
foo = "bar"
|
||||
}
|
||||
|
||||
resource "aws_security_group" "web" {}
|
7
config/testdata/import/one.tf
vendored
7
config/testdata/import/one.tf
vendored
@ -1,7 +0,0 @@
|
||||
variable "bar" {}
|
||||
|
||||
provider "aws" {
|
||||
bar = "baz";
|
||||
}
|
||||
|
||||
resource "aws_security_group" "db" {}
|
1
config/testdata/interpolations/concat.hcl
vendored
1
config/testdata/interpolations/concat.hcl
vendored
@ -1 +0,0 @@
|
||||
concat("foo","-","0.0/16")
|
5
config/testdata/lifecycle_cbd_typo.tf
vendored
5
config/testdata/lifecycle_cbd_typo.tf
vendored
@ -1,5 +0,0 @@
|
||||
resource "foo" "bar" {
|
||||
lifecycle {
|
||||
create_before_destroyy = false
|
||||
}
|
||||
}
|
7
config/testdata/module-providers/main.tf
vendored
7
config/testdata/module-providers/main.tf
vendored
@ -1,7 +0,0 @@
|
||||
module "child" {
|
||||
source = "./child"
|
||||
version = "0.1.2"
|
||||
providers = {
|
||||
"aws" = "aws.foo"
|
||||
}
|
||||
}
|
7
config/testdata/module-unnamed.tf
vendored
7
config/testdata/module-unnamed.tf
vendored
@ -1,7 +0,0 @@
|
||||
module "okay" {
|
||||
source = "./okay"
|
||||
}
|
||||
|
||||
module {
|
||||
source = "./not-okay"
|
||||
}
|
4
config/testdata/modules.tf
vendored
4
config/testdata/modules.tf
vendored
@ -1,4 +0,0 @@
|
||||
module "bar" {
|
||||
memory = "1G"
|
||||
source = "baz"
|
||||
}
|
4
config/testdata/output-depends-on.tf
vendored
4
config/testdata/output-depends-on.tf
vendored
@ -1,4 +0,0 @@
|
||||
output "value" {
|
||||
value = "foo"
|
||||
depends_on = ["foo"]
|
||||
}
|
14
config/testdata/output-no-warnings/main.tf
vendored
14
config/testdata/output-no-warnings/main.tf
vendored
@ -1,14 +0,0 @@
|
||||
resource "test_instance" "foo" {
|
||||
}
|
||||
|
||||
output "foo_id" {
|
||||
value = "${test_instance.foo.id}"
|
||||
}
|
||||
|
||||
resource "test_instance" "bar" {
|
||||
count = 3
|
||||
}
|
||||
|
||||
output "bar_count" {
|
||||
value = "${test_instance.bar.count}"
|
||||
}
|
7
config/testdata/output-unnamed.tf
vendored
7
config/testdata/output-unnamed.tf
vendored
@ -1,7 +0,0 @@
|
||||
output "okay" {
|
||||
value = "bar"
|
||||
}
|
||||
|
||||
output {
|
||||
value = "foo"
|
||||
}
|
24
config/testdata/output-warnings/main.tf
vendored
24
config/testdata/output-warnings/main.tf
vendored
@ -1,24 +0,0 @@
|
||||
locals {
|
||||
"count" = 1
|
||||
}
|
||||
|
||||
resource "test_instance" "foo" {
|
||||
count = "${local.count}"
|
||||
}
|
||||
|
||||
output "foo_id" {
|
||||
value = "${test_instance.foo.id}"
|
||||
}
|
||||
|
||||
variable "condition" {
|
||||
default = "true"
|
||||
}
|
||||
|
||||
resource "test_instance" "bar" {
|
||||
count = "${var.condition ? 1 : 0}"
|
||||
}
|
||||
|
||||
|
||||
output "bar_id" {
|
||||
value = "${test_instance.bar.id}"
|
||||
}
|
10
config/testdata/prevent-destroy-string.tf
vendored
10
config/testdata/prevent-destroy-string.tf
vendored
@ -1,10 +0,0 @@
|
||||
resource "aws_instance" "web" {
|
||||
ami = "foo"
|
||||
lifecycle {
|
||||
prevent_destroy = "true"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_instance" "bar" {
|
||||
ami = "foo"
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
provider "aws" {
|
||||
version = "bananas"
|
||||
a = "a"
|
||||
b = "b"
|
||||
}
|
6
config/testdata/provider-version/main.tf
vendored
6
config/testdata/provider-version/main.tf
vendored
@ -1,6 +0,0 @@
|
||||
provider "aws" {
|
||||
version = "0.0.1"
|
||||
a = "a"
|
||||
b = "b"
|
||||
}
|
||||
|
14
config/testdata/provisioners-destroy.tf
vendored
14
config/testdata/provisioners-destroy.tf
vendored
@ -1,14 +0,0 @@
|
||||
resource "aws_instance" "web" {
|
||||
provisioner "shell" {}
|
||||
|
||||
provisioner "shell" {
|
||||
path = "foo"
|
||||
when = "destroy"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
path = "foo"
|
||||
when = "destroy"
|
||||
on_failure = "continue"
|
||||
}
|
||||
}
|
11
config/testdata/provisioners.tf
vendored
11
config/testdata/provisioners.tf
vendored
@ -1,11 +0,0 @@
|
||||
resource "aws_instance" "web" {
|
||||
ami = "${var.foo}"
|
||||
security_groups = [
|
||||
"foo",
|
||||
"${aws_security_group.firewall.foo}"
|
||||
]
|
||||
|
||||
provisioner "shell" {
|
||||
path = "foo"
|
||||
}
|
||||
}
|
5
config/testdata/resource-arity-mistake.tf
vendored
5
config/testdata/resource-arity-mistake.tf
vendored
@ -1,5 +0,0 @@
|
||||
# I forgot the resource name!
|
||||
resource "aws_instance" {
|
||||
ami = "ami-abc123"
|
||||
instance_type = "t2.micro"
|
||||
}
|
4
config/testdata/resource-multi-lifecycle.tf
vendored
4
config/testdata/resource-multi-lifecycle.tf
vendored
@ -1,4 +0,0 @@
|
||||
resource "aws_instance" "foo" {
|
||||
lifecycle {}
|
||||
lifecycle {}
|
||||
}
|
11
config/testdata/resource-no-name.tf.json
vendored
11
config/testdata/resource-no-name.tf.json
vendored
@ -1,11 +0,0 @@
|
||||
{
|
||||
"resource" : {
|
||||
"aws_security_group" : {
|
||||
"allow_external_http_https" : {
|
||||
"tags" : {
|
||||
"Name" : "allow_external_http_https"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
config/testdata/terraform-backend-2.tf.json
vendored
9
config/testdata/terraform-backend-2.tf.json
vendored
@ -1,9 +0,0 @@
|
||||
{
|
||||
"terraform": {
|
||||
"backend": {
|
||||
"s3": {
|
||||
"foo": "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
4
config/testdata/terraform-backend-multi.tf
vendored
4
config/testdata/terraform-backend-multi.tf
vendored
@ -1,4 +0,0 @@
|
||||
terraform {
|
||||
backend "s3" {}
|
||||
backend "s4" {}
|
||||
}
|
5
config/testdata/terraform-backend.tf
vendored
5
config/testdata/terraform-backend.tf
vendored
@ -1,5 +0,0 @@
|
||||
terraform {
|
||||
backend "s3" {
|
||||
foo = "bar"
|
||||
}
|
||||
}
|
9
config/testdata/terraform-backend.tf.json
vendored
9
config/testdata/terraform-backend.tf.json
vendored
@ -1,9 +0,0 @@
|
||||
{
|
||||
"terraform": [{
|
||||
"backend": [{
|
||||
"s3": {
|
||||
"foo": "bar"
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
terraform {
|
||||
backend "foo" {
|
||||
key = "${var.var}"
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
resource "aws_instance" "web" {
|
||||
depends_on = ["aws_instance.db"]
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
resource "aws_instance" "web" {
|
||||
count = 5
|
||||
}
|
||||
|
||||
output "ip" {
|
||||
value = "${aws_instance.web.id}"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
terraform {
|
||||
required_version = "nope"
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
resource "aws_instance" "web" {
|
||||
provisioner "shell" {}
|
||||
|
||||
provisioner "shell" {
|
||||
path = "foo"
|
||||
when = "destroy"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
path = "foo"
|
||||
when = "destroy"
|
||||
on_failure = "continue"
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
resource "aws_instance" "foo" {
|
||||
}
|
||||
|
||||
output "no_count_in_output" {
|
||||
value = "${count.index}"
|
||||
}
|
||||
|
||||
module "no_count_in_module" {
|
||||
source = "./child"
|
||||
somevar = "${count.index}"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
resource "aws_instance" "web" {
|
||||
count = -1
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user