]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/config/loader.go
update vendor and go.mod
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / config / loader.go
CommitLineData
bae9f6d2
JC
1package config
2
3import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "os"
8 "path/filepath"
9 "sort"
10 "strings"
11
12 "github.com/hashicorp/hcl"
13)
14
15// ErrNoConfigsFound is the error returned by LoadDir if no
16// Terraform configuration files were found in the given directory.
17type ErrNoConfigsFound struct {
18 Dir string
19}
20
21func (e ErrNoConfigsFound) Error() string {
22 return fmt.Sprintf(
23 "No Terraform configuration files found in directory: %s",
24 e.Dir)
25}
26
27// LoadJSON loads a single Terraform configuration from a given JSON document.
28//
29// The document must be a complete Terraform configuration. This function will
30// NOT try to load any additional modules so only the given document is loaded.
31func LoadJSON(raw json.RawMessage) (*Config, error) {
32 obj, err := hcl.Parse(string(raw))
33 if err != nil {
34 return nil, fmt.Errorf(
35 "Error parsing JSON document as HCL: %s", err)
36 }
37
38 // Start building the result
39 hclConfig := &hclConfigurable{
40 Root: obj,
41 }
42
43 return hclConfig.Config()
44}
45
46// LoadFile loads the Terraform configuration from a given file.
47//
48// This file can be any format that Terraform recognizes, and import any
49// other format that Terraform recognizes.
50func LoadFile(path string) (*Config, error) {
51 importTree, err := loadTree(path)
52 if err != nil {
53 return nil, err
54 }
55
56 configTree, err := importTree.ConfigTree()
57
58 // Close the importTree now so that we can clear resources as quickly
59 // as possible.
60 importTree.Close()
61
62 if err != nil {
63 return nil, err
64 }
65
66 return configTree.Flatten()
67}
68
69// LoadDir loads all the Terraform configuration files in a single
70// directory and appends them together.
71//
72// Special files known as "override files" can also be present, which
73// are merged into the loaded configuration. That is, the non-override
74// files are loaded first to create the configuration. Then, the overrides
75// are merged into the configuration to create the final configuration.
76//
77// Files are loaded in lexical order.
78func LoadDir(root string) (*Config, error) {
79 files, overrides, err := dirFiles(root)
80 if err != nil {
81 return nil, err
82 }
15c0b25d 83 if len(files) == 0 && len(overrides) == 0 {
bae9f6d2
JC
84 return nil, &ErrNoConfigsFound{Dir: root}
85 }
86
87 // Determine the absolute path to the directory.
88 rootAbs, err := filepath.Abs(root)
89 if err != nil {
90 return nil, err
91 }
92
93 var result *Config
94
95 // Sort the files and overrides so we have a deterministic order
96 sort.Strings(files)
97 sort.Strings(overrides)
98
99 // Load all the regular files, append them to each other.
100 for _, f := range files {
101 c, err := LoadFile(f)
102 if err != nil {
103 return nil, err
104 }
105
106 if result != nil {
107 result, err = Append(result, c)
108 if err != nil {
109 return nil, err
110 }
111 } else {
112 result = c
113 }
114 }
15c0b25d
AP
115 if len(files) == 0 {
116 result = &Config{}
117 }
bae9f6d2
JC
118
119 // Load all the overrides, and merge them into the config
120 for _, f := range overrides {
121 c, err := LoadFile(f)
122 if err != nil {
123 return nil, err
124 }
125
126 result, err = Merge(result, c)
127 if err != nil {
128 return nil, err
129 }
130 }
131
132 // Mark the directory
133 result.Dir = rootAbs
134
135 return result, nil
136}
137
bae9f6d2
JC
138// Ext returns the Terraform configuration extension of the given
139// path, or a blank string if it is an invalid function.
140func ext(path string) string {
141 if strings.HasSuffix(path, ".tf") {
142 return ".tf"
143 } else if strings.HasSuffix(path, ".tf.json") {
144 return ".tf.json"
145 } else {
146 return ""
147 }
148}
149
150func dirFiles(dir string) ([]string, []string, error) {
151 f, err := os.Open(dir)
152 if err != nil {
153 return nil, nil, err
154 }
155 defer f.Close()
156
157 fi, err := f.Stat()
158 if err != nil {
159 return nil, nil, err
160 }
161 if !fi.IsDir() {
162 return nil, nil, fmt.Errorf(
163 "configuration path must be a directory: %s",
164 dir)
165 }
166
167 var files, overrides []string
168 err = nil
169 for err != io.EOF {
170 var fis []os.FileInfo
171 fis, err = f.Readdir(128)
172 if err != nil && err != io.EOF {
173 return nil, nil, err
174 }
175
176 for _, fi := range fis {
177 // Ignore directories
178 if fi.IsDir() {
179 continue
180 }
181
182 // Only care about files that are valid to load
183 name := fi.Name()
184 extValue := ext(name)
c680a8e1 185 if extValue == "" || IsIgnoredFile(name) {
bae9f6d2
JC
186 continue
187 }
188
189 // Determine if we're dealing with an override
190 nameNoExt := name[:len(name)-len(extValue)]
191 override := nameNoExt == "override" ||
192 strings.HasSuffix(nameNoExt, "_override")
193
194 path := filepath.Join(dir, name)
195 if override {
196 overrides = append(overrides, path)
197 } else {
198 files = append(files, path)
199 }
200 }
201 }
202
203 return files, overrides, nil
204}
205
c680a8e1 206// IsIgnoredFile returns true or false depending on whether the
bae9f6d2 207// provided file name is a file that should be ignored.
c680a8e1 208func IsIgnoredFile(name string) bool {
bae9f6d2
JC
209 return strings.HasPrefix(name, ".") || // Unix-like hidden files
210 strings.HasSuffix(name, "~") || // vim
211 strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#") // emacs
212}