diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go new file mode 100644 index 0000000..770d5df --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go | |||
@@ -0,0 +1,144 @@ | |||
1 | package earlyconfig | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "sort" | ||
6 | "strings" | ||
7 | |||
8 | version "github.com/hashicorp/go-version" | ||
9 | "github.com/hashicorp/terraform-config-inspect/tfconfig" | ||
10 | "github.com/hashicorp/terraform/addrs" | ||
11 | "github.com/hashicorp/terraform/tfdiags" | ||
12 | ) | ||
13 | |||
14 | // BuildConfig constructs a Config from a root module by loading all of its | ||
15 | // descendent modules via the given ModuleWalker. | ||
16 | func BuildConfig(root *tfconfig.Module, walker ModuleWalker) (*Config, tfdiags.Diagnostics) { | ||
17 | var diags tfdiags.Diagnostics | ||
18 | cfg := &Config{ | ||
19 | Module: root, | ||
20 | } | ||
21 | cfg.Root = cfg // Root module is self-referential. | ||
22 | cfg.Children, diags = buildChildModules(cfg, walker) | ||
23 | return cfg, diags | ||
24 | } | ||
25 | |||
26 | func buildChildModules(parent *Config, walker ModuleWalker) (map[string]*Config, tfdiags.Diagnostics) { | ||
27 | var diags tfdiags.Diagnostics | ||
28 | ret := map[string]*Config{} | ||
29 | calls := parent.Module.ModuleCalls | ||
30 | |||
31 | // We'll sort the calls by their local names so that they'll appear in a | ||
32 | // predictable order in any logging that's produced during the walk. | ||
33 | callNames := make([]string, 0, len(calls)) | ||
34 | for k := range calls { | ||
35 | callNames = append(callNames, k) | ||
36 | } | ||
37 | sort.Strings(callNames) | ||
38 | |||
39 | for _, callName := range callNames { | ||
40 | call := calls[callName] | ||
41 | path := make([]string, len(parent.Path)+1) | ||
42 | copy(path, parent.Path) | ||
43 | path[len(path)-1] = call.Name | ||
44 | |||
45 | var vc version.Constraints | ||
46 | if strings.TrimSpace(call.Version) != "" { | ||
47 | var err error | ||
48 | vc, err = version.NewConstraint(call.Version) | ||
49 | if err != nil { | ||
50 | diags = diags.Append(wrapDiagnostic(tfconfig.Diagnostic{ | ||
51 | Severity: tfconfig.DiagError, | ||
52 | Summary: "Invalid version constraint", | ||
53 | Detail: fmt.Sprintf("Module %q (declared at %s line %d) has invalid version constraint %q: %s.", callName, call.Pos.Filename, call.Pos.Line, call.Version, err), | ||
54 | })) | ||
55 | continue | ||
56 | } | ||
57 | } | ||
58 | |||
59 | req := ModuleRequest{ | ||
60 | Name: call.Name, | ||
61 | Path: path, | ||
62 | SourceAddr: call.Source, | ||
63 | VersionConstraints: vc, | ||
64 | Parent: parent, | ||
65 | CallPos: call.Pos, | ||
66 | } | ||
67 | |||
68 | mod, ver, modDiags := walker.LoadModule(&req) | ||
69 | diags = append(diags, modDiags...) | ||
70 | if mod == nil { | ||
71 | // nil can be returned if the source address was invalid and so | ||
72 | // nothing could be loaded whatsoever. LoadModule should've | ||
73 | // returned at least one error diagnostic in that case. | ||
74 | continue | ||
75 | } | ||
76 | |||
77 | child := &Config{ | ||
78 | Parent: parent, | ||
79 | Root: parent.Root, | ||
80 | Path: path, | ||
81 | Module: mod, | ||
82 | CallPos: call.Pos, | ||
83 | SourceAddr: call.Source, | ||
84 | Version: ver, | ||
85 | } | ||
86 | |||
87 | child.Children, modDiags = buildChildModules(child, walker) | ||
88 | diags = diags.Append(modDiags) | ||
89 | |||
90 | ret[call.Name] = child | ||
91 | } | ||
92 | |||
93 | return ret, diags | ||
94 | } | ||
95 | |||
96 | // ModuleRequest is used as part of the ModuleWalker interface used with | ||
97 | // function BuildConfig. | ||
98 | type ModuleRequest struct { | ||
99 | // Name is the "logical name" of the module call within configuration. | ||
100 | // This is provided in case the name is used as part of a storage key | ||
101 | // for the module, but implementations must otherwise treat it as an | ||
102 | // opaque string. It is guaranteed to have already been validated as an | ||
103 | // HCL identifier and UTF-8 encoded. | ||
104 | Name string | ||
105 | |||
106 | // Path is a list of logical names that traverse from the root module to | ||
107 | // this module. This can be used, for example, to form a lookup key for | ||
108 | // each distinct module call in a configuration, allowing for multiple | ||
109 | // calls with the same name at different points in the tree. | ||
110 | Path addrs.Module | ||
111 | |||
112 | // SourceAddr is the source address string provided by the user in | ||
113 | // configuration. | ||
114 | SourceAddr string | ||
115 | |||
116 | // VersionConstraint is the version constraint applied to the module in | ||
117 | // configuration. | ||
118 | VersionConstraints version.Constraints | ||
119 | |||
120 | // Parent is the partially-constructed module tree node that the loaded | ||
121 | // module will be added to. Callers may refer to any field of this | ||
122 | // structure except Children, which is still under construction when | ||
123 | // ModuleRequest objects are created and thus has undefined content. | ||
124 | // The main reason this is provided is so that full module paths can | ||
125 | // be constructed for uniqueness. | ||
126 | Parent *Config | ||
127 | |||
128 | // CallRange is the source position for the header of the "module" block | ||
129 | // in configuration that prompted this request. | ||
130 | CallPos tfconfig.SourcePos | ||
131 | } | ||
132 | |||
133 | // ModuleWalker is an interface used with BuildConfig. | ||
134 | type ModuleWalker interface { | ||
135 | LoadModule(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) | ||
136 | } | ||
137 | |||
138 | // ModuleWalkerFunc is an implementation of ModuleWalker that directly wraps | ||
139 | // a callback function, for more convenient use of that interface. | ||
140 | type ModuleWalkerFunc func(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) | ||
141 | |||
142 | func (f ModuleWalkerFunc) LoadModule(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) { | ||
143 | return f(req) | ||
144 | } | ||