]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/configs/config_build.go
Merge pull request #32 from ndench/0.12-compatibility
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / configs / config_build.go
1 package configs
2
3 import (
4 "sort"
5
6 version "github.com/hashicorp/go-version"
7 "github.com/hashicorp/hcl2/hcl"
8 "github.com/hashicorp/terraform/addrs"
9 )
10
11 // BuildConfig constructs a Config from a root module by loading all of its
12 // descendent modules via the given ModuleWalker.
13 //
14 // The result is a module tree that has so far only had basic module- and
15 // file-level invariants validated. If the returned diagnostics contains errors,
16 // the returned module tree may be incomplete but can still be used carefully
17 // for static analysis.
18 func BuildConfig(root *Module, walker ModuleWalker) (*Config, hcl.Diagnostics) {
19 var diags hcl.Diagnostics
20 cfg := &Config{
21 Module: root,
22 }
23 cfg.Root = cfg // Root module is self-referential.
24 cfg.Children, diags = buildChildModules(cfg, walker)
25 return cfg, diags
26 }
27
28 func buildChildModules(parent *Config, walker ModuleWalker) (map[string]*Config, hcl.Diagnostics) {
29 var diags hcl.Diagnostics
30 ret := map[string]*Config{}
31
32 calls := parent.Module.ModuleCalls
33
34 // We'll sort the calls by their local names so that they'll appear in a
35 // predictable order in any logging that's produced during the walk.
36 callNames := make([]string, 0, len(calls))
37 for k := range calls {
38 callNames = append(callNames, k)
39 }
40 sort.Strings(callNames)
41
42 for _, callName := range callNames {
43 call := calls[callName]
44 path := make([]string, len(parent.Path)+1)
45 copy(path, parent.Path)
46 path[len(path)-1] = call.Name
47
48 req := ModuleRequest{
49 Name: call.Name,
50 Path: path,
51 SourceAddr: call.SourceAddr,
52 SourceAddrRange: call.SourceAddrRange,
53 VersionConstraint: call.Version,
54 Parent: parent,
55 CallRange: call.DeclRange,
56 }
57
58 mod, ver, modDiags := walker.LoadModule(&req)
59 diags = append(diags, modDiags...)
60 if mod == nil {
61 // nil can be returned if the source address was invalid and so
62 // nothing could be loaded whatsoever. LoadModule should've
63 // returned at least one error diagnostic in that case.
64 continue
65 }
66
67 child := &Config{
68 Parent: parent,
69 Root: parent.Root,
70 Path: path,
71 Module: mod,
72 CallRange: call.DeclRange,
73 SourceAddr: call.SourceAddr,
74 SourceAddrRange: call.SourceAddrRange,
75 Version: ver,
76 }
77
78 child.Children, modDiags = buildChildModules(child, walker)
79
80 ret[call.Name] = child
81 }
82
83 return ret, diags
84 }
85
86 // A ModuleWalker knows how to find and load a child module given details about
87 // the module to be loaded and a reference to its partially-loaded parent
88 // Config.
89 type ModuleWalker interface {
90 // LoadModule finds and loads a requested child module.
91 //
92 // If errors are detected during loading, implementations should return them
93 // in the diagnostics object. If the diagnostics object contains any errors
94 // then the caller will tolerate the returned module being nil or incomplete.
95 // If no errors are returned, it should be non-nil and complete.
96 //
97 // Full validation need not have been performed but an implementation should
98 // ensure that the basic file- and module-validations performed by the
99 // LoadConfigDir function (valid syntax, no namespace collisions, etc) have
100 // been performed before returning a module.
101 LoadModule(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics)
102 }
103
104 // ModuleWalkerFunc is an implementation of ModuleWalker that directly wraps
105 // a callback function, for more convenient use of that interface.
106 type ModuleWalkerFunc func(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics)
107
108 // LoadModule implements ModuleWalker.
109 func (f ModuleWalkerFunc) LoadModule(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) {
110 return f(req)
111 }
112
113 // ModuleRequest is used with the ModuleWalker interface to describe a child
114 // module that must be loaded.
115 type ModuleRequest struct {
116 // Name is the "logical name" of the module call within configuration.
117 // This is provided in case the name is used as part of a storage key
118 // for the module, but implementations must otherwise treat it as an
119 // opaque string. It is guaranteed to have already been validated as an
120 // HCL identifier and UTF-8 encoded.
121 Name string
122
123 // Path is a list of logical names that traverse from the root module to
124 // this module. This can be used, for example, to form a lookup key for
125 // each distinct module call in a configuration, allowing for multiple
126 // calls with the same name at different points in the tree.
127 Path addrs.Module
128
129 // SourceAddr is the source address string provided by the user in
130 // configuration.
131 SourceAddr string
132
133 // SourceAddrRange is the source range for the SourceAddr value as it
134 // was provided in configuration. This can and should be used to generate
135 // diagnostics about the source address having invalid syntax, referring
136 // to a non-existent object, etc.
137 SourceAddrRange hcl.Range
138
139 // VersionConstraint is the version constraint applied to the module in
140 // configuration. This data structure includes the source range for
141 // the constraint, which can and should be used to generate diagnostics
142 // about constraint-related issues, such as constraints that eliminate all
143 // available versions of a module whose source is otherwise valid.
144 VersionConstraint VersionConstraint
145
146 // Parent is the partially-constructed module tree node that the loaded
147 // module will be added to. Callers may refer to any field of this
148 // structure except Children, which is still under construction when
149 // ModuleRequest objects are created and thus has undefined content.
150 // The main reason this is provided is so that full module paths can
151 // be constructed for uniqueness.
152 Parent *Config
153
154 // CallRange is the source range for the header of the "module" block
155 // in configuration that prompted this request. This can be used as the
156 // subject of an error diagnostic that relates to the module call itself,
157 // rather than to either its source address or its version number.
158 CallRange hcl.Range
159 }
160
161 // DisabledModuleWalker is a ModuleWalker that doesn't support
162 // child modules at all, and so will return an error if asked to load one.
163 //
164 // This is provided primarily for testing. There is no good reason to use this
165 // in the main application.
166 var DisabledModuleWalker ModuleWalker
167
168 func init() {
169 DisabledModuleWalker = ModuleWalkerFunc(func(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) {
170 return nil, nil, hcl.Diagnostics{
171 {
172 Severity: hcl.DiagError,
173 Summary: "Child modules are not supported",
174 Detail: "Child module calls are not allowed in this context.",
175 Subject: &req.CallRange,
176 },
177 }
178 })
179 }