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 "" } 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) }