opentofu/config/config_string.go
Martin Atkins fc4fa10981 config: "ResourceMode" concept for resources
Previously resources were assumed to always support the full set of
create, read, update and delete operations, and Terraform's resource
management lifecycle.

Data sources introduce a new kind of resource that only supports the
"read" operation. To support this, a new "Mode" field is added to
the Resource concept within the config layer, which can be set to
ManagedResourceMode (to indicate the only mode previously possible) or
DataResourceMode (to indicate that only "read" is supported).

To support both managed and data resources in the tests, the
stringification of resources in config_string.go is adjusted slightly
to use the Id() method rather than the unusual type[name] serialization
from before, causing a simple mechanical adjustment to the loader tests'
expected result strings.
2016-05-14 08:26:35 -07:00

303 lines
6.0 KiB
Go

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 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.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)
}
}
}
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 {
result += fmt.Sprintf(" %s\n", p.Type)
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)
}