]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform-config-inspect/tfconfig/load_legacy.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform-config-inspect / tfconfig / load_legacy.go
1 package tfconfig
2
3 import (
4 "io/ioutil"
5 "strings"
6
7 legacyhcl "github.com/hashicorp/hcl"
8 legacyast "github.com/hashicorp/hcl/hcl/ast"
9 )
10
11 func loadModuleLegacyHCL(dir string) (*Module, Diagnostics) {
12 // This implementation is intentionally more quick-and-dirty than the
13 // main loader. In particular, it doesn't bother to keep careful track
14 // of multiple error messages because we always fall back on returning
15 // the main parser's error message if our fallback parsing produces
16 // an error, and thus the errors here are not seen by the end-caller.
17 mod := newModule(dir)
18
19 primaryPaths, diags := dirFiles(dir)
20 if diags.HasErrors() {
21 return mod, diagnosticsHCL(diags)
22 }
23
24 for _, filename := range primaryPaths {
25 src, err := ioutil.ReadFile(filename)
26 if err != nil {
27 return mod, diagnosticsErrorf("Error reading %s: %s", filename, err)
28 }
29
30 hclRoot, err := legacyhcl.Parse(string(src))
31 if err != nil {
32 return mod, diagnosticsErrorf("Error parsing %s: %s", filename, err)
33 }
34
35 list, ok := hclRoot.Node.(*legacyast.ObjectList)
36 if !ok {
37 return mod, diagnosticsErrorf("Error parsing %s: no root object", filename)
38 }
39
40 for _, item := range list.Filter("terraform").Items {
41 if len(item.Keys) > 0 {
42 item = &legacyast.ObjectItem{
43 Val: &legacyast.ObjectType{
44 List: &legacyast.ObjectList{
45 Items: []*legacyast.ObjectItem{item},
46 },
47 },
48 }
49 }
50
51 type TerraformBlock struct {
52 RequiredVersion string `hcl:"required_version"`
53 }
54 var block TerraformBlock
55 err = legacyhcl.DecodeObject(&block, item.Val)
56 if err != nil {
57 return nil, diagnosticsErrorf("terraform block: %s", err)
58 }
59
60 if block.RequiredVersion != "" {
61 mod.RequiredCore = append(mod.RequiredCore, block.RequiredVersion)
62 }
63 }
64
65 if vars := list.Filter("variable"); len(vars.Items) > 0 {
66 vars = vars.Children()
67 type VariableBlock struct {
68 Type string `hcl:"type"`
69 Default interface{}
70 Description string
71 Fields []string `hcl:",decodedFields"`
72 }
73
74 for _, item := range vars.Items {
75 unwrapLegacyHCLObjectKeysFromJSON(item, 1)
76
77 if len(item.Keys) != 1 {
78 return nil, diagnosticsErrorf("variable block at %s has no label", item.Pos())
79 }
80
81 name := item.Keys[0].Token.Value().(string)
82
83 var block VariableBlock
84 err := legacyhcl.DecodeObject(&block, item.Val)
85 if err != nil {
86 return nil, diagnosticsErrorf("invalid variable block at %s: %s", item.Pos(), err)
87 }
88
89 // Clean up legacy HCL decoding ambiguity by unwrapping list of maps
90 if ms, ok := block.Default.([]map[string]interface{}); ok {
91 def := make(map[string]interface{})
92 for _, m := range ms {
93 for k, v := range m {
94 def[k] = v
95 }
96 }
97 block.Default = def
98 }
99
100 v := &Variable{
101 Name: name,
102 Type: block.Type,
103 Description: block.Description,
104 Default: block.Default,
105 Pos: sourcePosLegacyHCL(item.Pos(), filename),
106 }
107 if _, exists := mod.Variables[name]; exists {
108 return nil, diagnosticsErrorf("duplicate variable block for %q", name)
109 }
110 mod.Variables[name] = v
111
112 }
113 }
114
115 if outputs := list.Filter("output"); len(outputs.Items) > 0 {
116 outputs = outputs.Children()
117 type OutputBlock struct {
118 Description string
119 }
120
121 for _, item := range outputs.Items {
122 unwrapLegacyHCLObjectKeysFromJSON(item, 1)
123
124 if len(item.Keys) != 1 {
125 return nil, diagnosticsErrorf("output block at %s has no label", item.Pos())
126 }
127
128 name := item.Keys[0].Token.Value().(string)
129
130 var block OutputBlock
131 err := legacyhcl.DecodeObject(&block, item.Val)
132 if err != nil {
133 return nil, diagnosticsErrorf("invalid output block at %s: %s", item.Pos(), err)
134 }
135
136 o := &Output{
137 Name: name,
138 Description: block.Description,
139 Pos: sourcePosLegacyHCL(item.Pos(), filename),
140 }
141 if _, exists := mod.Outputs[name]; exists {
142 return nil, diagnosticsErrorf("duplicate output block for %q", name)
143 }
144 mod.Outputs[name] = o
145 }
146 }
147
148 for _, blockType := range []string{"resource", "data"} {
149 if resources := list.Filter(blockType); len(resources.Items) > 0 {
150 resources = resources.Children()
151 type ResourceBlock struct {
152 Provider string
153 }
154
155 for _, item := range resources.Items {
156 unwrapLegacyHCLObjectKeysFromJSON(item, 2)
157
158 if len(item.Keys) != 2 {
159 return nil, diagnosticsErrorf("resource block at %s has wrong label count", item.Pos())
160 }
161
162 typeName := item.Keys[0].Token.Value().(string)
163 name := item.Keys[1].Token.Value().(string)
164 var mode ResourceMode
165 var rMap map[string]*Resource
166 switch blockType {
167 case "resource":
168 mode = ManagedResourceMode
169 rMap = mod.ManagedResources
170 case "data":
171 mode = DataResourceMode
172 rMap = mod.DataResources
173 }
174
175 var block ResourceBlock
176 err := legacyhcl.DecodeObject(&block, item.Val)
177 if err != nil {
178 return nil, diagnosticsErrorf("invalid resource block at %s: %s", item.Pos(), err)
179 }
180
181 var providerName, providerAlias string
182 if dotPos := strings.IndexByte(block.Provider, '.'); dotPos != -1 {
183 providerName = block.Provider[:dotPos]
184 providerAlias = block.Provider[dotPos+1:]
185 } else {
186 providerName = block.Provider
187 }
188 if providerName == "" {
189 providerName = resourceTypeDefaultProviderName(typeName)
190 }
191
192 r := &Resource{
193 Mode: mode,
194 Type: typeName,
195 Name: name,
196 Provider: ProviderRef{
197 Name: providerName,
198 Alias: providerAlias,
199 },
200 Pos: sourcePosLegacyHCL(item.Pos(), filename),
201 }
202 key := r.MapKey()
203 if _, exists := rMap[key]; exists {
204 return nil, diagnosticsErrorf("duplicate resource block for %q", key)
205 }
206 rMap[key] = r
207 }
208 }
209
210 }
211
212 if moduleCalls := list.Filter("module"); len(moduleCalls.Items) > 0 {
213 moduleCalls = moduleCalls.Children()
214 type ModuleBlock struct {
215 Source string
216 Version string
217 }
218
219 for _, item := range moduleCalls.Items {
220 unwrapLegacyHCLObjectKeysFromJSON(item, 1)
221
222 if len(item.Keys) != 1 {
223 return nil, diagnosticsErrorf("module block at %s has no label", item.Pos())
224 }
225
226 name := item.Keys[0].Token.Value().(string)
227
228 var block ModuleBlock
229 err := legacyhcl.DecodeObject(&block, item.Val)
230 if err != nil {
231 return nil, diagnosticsErrorf("module block at %s: %s", item.Pos(), err)
232 }
233
234 mc := &ModuleCall{
235 Name: name,
236 Source: block.Source,
237 Version: block.Version,
238 Pos: sourcePosLegacyHCL(item.Pos(), filename),
239 }
240 // it's possible this module call is from an override file
241 if origMod, exists := mod.ModuleCalls[name]; exists {
242 if mc.Source == "" {
243 mc.Source = origMod.Source
244 }
245 }
246 mod.ModuleCalls[name] = mc
247 }
248 }
249
250 if providerConfigs := list.Filter("provider"); len(providerConfigs.Items) > 0 {
251 providerConfigs = providerConfigs.Children()
252 type ProviderBlock struct {
253 Version string
254 }
255
256 for _, item := range providerConfigs.Items {
257 unwrapLegacyHCLObjectKeysFromJSON(item, 1)
258
259 if len(item.Keys) != 1 {
260 return nil, diagnosticsErrorf("provider block at %s has no label", item.Pos())
261 }
262
263 name := item.Keys[0].Token.Value().(string)
264
265 var block ProviderBlock
266 err := legacyhcl.DecodeObject(&block, item.Val)
267 if err != nil {
268 return nil, diagnosticsErrorf("invalid provider block at %s: %s", item.Pos(), err)
269 }
270
271 if block.Version != "" {
272 mod.RequiredProviders[name] = append(mod.RequiredProviders[name], block.Version)
273 }
274
275 // Even if there wasn't an explicit version required, we still
276 // need an entry in our map to signal the unversioned dependency.
277 if _, exists := mod.RequiredProviders[name]; !exists {
278 mod.RequiredProviders[name] = []string{}
279 }
280
281 }
282 }
283 }
284
285 return mod, nil
286 }
287
288 // unwrapLegacyHCLObjectKeysFromJSON cleans up an edge case that can occur when
289 // parsing JSON as input: if we're parsing JSON then directly nested
290 // items will show up as additional "keys".
291 //
292 // For objects that expect a fixed number of keys, this breaks the
293 // decoding process. This function unwraps the object into what it would've
294 // looked like if it came directly from HCL by specifying the number of keys
295 // you expect.
296 //
297 // Example:
298 //
299 // { "foo": { "baz": {} } }
300 //
301 // Will show up with Keys being: []string{"foo", "baz"}
302 // when we really just want the first two. This function will fix this.
303 func unwrapLegacyHCLObjectKeysFromJSON(item *legacyast.ObjectItem, depth int) {
304 if len(item.Keys) > depth && item.Keys[0].Token.JSON {
305 for len(item.Keys) > depth {
306 // Pop off the last key
307 n := len(item.Keys)
308 key := item.Keys[n-1]
309 item.Keys[n-1] = nil
310 item.Keys = item.Keys[:n-1]
311
312 // Wrap our value in a list
313 item.Val = &legacyast.ObjectType{
314 List: &legacyast.ObjectList{
315 Items: []*legacyast.ObjectItem{
316 &legacyast.ObjectItem{
317 Keys: []*legacyast.ObjectKey{key},
318 Val: item.Val,
319 },
320 },
321 },
322 }
323 }
324 }
325 }