]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform-config-inspect/tfconfig/load.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform-config-inspect / tfconfig / load.go
1 package tfconfig
2
3 import (
4 "fmt"
5 "io/ioutil"
6 "path/filepath"
7 "strings"
8
9 "github.com/hashicorp/hcl2/hcl"
10 )
11
12 // LoadModule reads the directory at the given path and attempts to interpret
13 // it as a Terraform module.
14 func LoadModule(dir string) (*Module, Diagnostics) {
15
16 // For broad compatibility here we actually have two separate loader
17 // codepaths. The main one uses the new HCL parser and API and is intended
18 // for configurations from Terraform 0.12 onwards (though will work for
19 // many older configurations too), but we'll also fall back on one that
20 // uses the _old_ HCL implementation so we can deal with some edge-cases
21 // that are not valid in new HCL.
22
23 module, diags := loadModule(dir)
24 if diags.HasErrors() {
25 // Try using the legacy HCL parser and see if we fare better.
26 legacyModule, legacyDiags := loadModuleLegacyHCL(dir)
27 if !legacyDiags.HasErrors() {
28 legacyModule.init(legacyDiags)
29 return legacyModule, legacyDiags
30 }
31 }
32
33 module.init(diags)
34 return module, diags
35 }
36
37 // IsModuleDir checks if the given path contains terraform configuration files.
38 // This allows the caller to decide how to handle directories that do not have tf files.
39 func IsModuleDir(dir string) bool {
40 primaryPaths, _ := dirFiles(dir)
41 if len(primaryPaths) == 0 {
42 return false
43 }
44 return true
45 }
46
47 func (m *Module) init(diags Diagnostics) {
48 // Fill in any additional provider requirements that are implied by
49 // resource configurations, to avoid the caller from needing to apply
50 // this logic itself. Implied requirements don't have version constraints,
51 // but we'll make sure the requirement value is still non-nil in this
52 // case so callers can easily recognize it.
53 for _, r := range m.ManagedResources {
54 if _, exists := m.RequiredProviders[r.Provider.Name]; !exists {
55 m.RequiredProviders[r.Provider.Name] = []string{}
56 }
57 }
58 for _, r := range m.DataResources {
59 if _, exists := m.RequiredProviders[r.Provider.Name]; !exists {
60 m.RequiredProviders[r.Provider.Name] = []string{}
61 }
62 }
63
64 // We redundantly also reference the diagnostics from inside the module
65 // object, primarily so that we can easily included in JSON-serialized
66 // versions of the module object.
67 m.Diagnostics = diags
68 }
69
70 func dirFiles(dir string) (primary []string, diags hcl.Diagnostics) {
71 infos, err := ioutil.ReadDir(dir)
72 if err != nil {
73 diags = append(diags, &hcl.Diagnostic{
74 Severity: hcl.DiagError,
75 Summary: "Failed to read module directory",
76 Detail: fmt.Sprintf("Module directory %s does not exist or cannot be read.", dir),
77 })
78 return
79 }
80
81 var override []string
82 for _, info := range infos {
83 if info.IsDir() {
84 // We only care about files
85 continue
86 }
87
88 name := info.Name()
89 ext := fileExt(name)
90 if ext == "" || isIgnoredFile(name) {
91 continue
92 }
93
94 baseName := name[:len(name)-len(ext)] // strip extension
95 isOverride := baseName == "override" || strings.HasSuffix(baseName, "_override")
96
97 fullPath := filepath.Join(dir, name)
98 if isOverride {
99 override = append(override, fullPath)
100 } else {
101 primary = append(primary, fullPath)
102 }
103 }
104
105 // We are assuming that any _override files will be logically named,
106 // and processing the files in alphabetical order. Primaries first, then overrides.
107 primary = append(primary, override...)
108
109 return
110 }
111
112 // fileExt returns the Terraform configuration extension of the given
113 // path, or a blank string if it is not a recognized extension.
114 func fileExt(path string) string {
115 if strings.HasSuffix(path, ".tf") {
116 return ".tf"
117 } else if strings.HasSuffix(path, ".tf.json") {
118 return ".tf.json"
119 } else {
120 return ""
121 }
122 }
123
124 // isIgnoredFile returns true if the given filename (which must not have a
125 // directory path ahead of it) should be ignored as e.g. an editor swap file.
126 func isIgnoredFile(name string) bool {
127 return strings.HasPrefix(name, ".") || // Unix-like hidden files
128 strings.HasSuffix(name, "~") || // vim
129 strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#") // emacs
130 }