diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/module/storage.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/config/module/storage.go | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/module/storage.go b/vendor/github.com/hashicorp/terraform/config/module/storage.go new file mode 100644 index 0000000..58e3a10 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/storage.go | |||
@@ -0,0 +1,365 @@ | |||
1 | package module | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | "io/ioutil" | ||
7 | "log" | ||
8 | "os" | ||
9 | "path/filepath" | ||
10 | "strings" | ||
11 | |||
12 | getter "github.com/hashicorp/go-getter" | ||
13 | "github.com/hashicorp/terraform/registry" | ||
14 | "github.com/hashicorp/terraform/registry/regsrc" | ||
15 | "github.com/hashicorp/terraform/svchost/disco" | ||
16 | "github.com/mitchellh/cli" | ||
17 | ) | ||
18 | |||
19 | const manifestName = "modules.json" | ||
20 | |||
21 | // moduleManifest is the serialization structure used to record the stored | ||
22 | // module's metadata. | ||
23 | type moduleManifest struct { | ||
24 | Modules []moduleRecord | ||
25 | } | ||
26 | |||
27 | // moduleRecords represents the stored module's metadata. | ||
28 | // This is compared for equality using '==', so all fields needs to remain | ||
29 | // comparable. | ||
30 | type moduleRecord struct { | ||
31 | // Source is the module source string from the config, minus any | ||
32 | // subdirectory. | ||
33 | Source string | ||
34 | |||
35 | // Key is the locally unique identifier for this module. | ||
36 | Key string | ||
37 | |||
38 | // Version is the exact version string for the stored module. | ||
39 | Version string | ||
40 | |||
41 | // Dir is the directory name returned by the FileStorage. This is what | ||
42 | // allows us to correlate a particular module version with the location on | ||
43 | // disk. | ||
44 | Dir string | ||
45 | |||
46 | // Root is the root directory containing the module. If the module is | ||
47 | // unpacked from an archive, and not located in the root directory, this is | ||
48 | // used to direct the loader to the correct subdirectory. This is | ||
49 | // independent from any subdirectory in the original source string, which | ||
50 | // may traverse further into the module tree. | ||
51 | Root string | ||
52 | |||
53 | // url is the location of the module source | ||
54 | url string | ||
55 | |||
56 | // Registry is true if this module is sourced from a registry | ||
57 | registry bool | ||
58 | } | ||
59 | |||
60 | // Storage implements methods to manage the storage of modules. | ||
61 | // This is used by Tree.Load to query registries, authenticate requests, and | ||
62 | // store modules locally. | ||
63 | type Storage struct { | ||
64 | // StorageDir is the full path to the directory where all modules will be | ||
65 | // stored. | ||
66 | StorageDir string | ||
67 | |||
68 | // Ui is an optional cli.Ui for user output | ||
69 | Ui cli.Ui | ||
70 | |||
71 | // Mode is the GetMode that will be used for various operations. | ||
72 | Mode GetMode | ||
73 | |||
74 | registry *registry.Client | ||
75 | } | ||
76 | |||
77 | // NewStorage returns a new initialized Storage object. | ||
78 | func NewStorage(dir string, services *disco.Disco) *Storage { | ||
79 | regClient := registry.NewClient(services, nil) | ||
80 | |||
81 | return &Storage{ | ||
82 | StorageDir: dir, | ||
83 | registry: regClient, | ||
84 | } | ||
85 | } | ||
86 | |||
87 | // loadManifest returns the moduleManifest file from the parent directory. | ||
88 | func (s Storage) loadManifest() (moduleManifest, error) { | ||
89 | manifest := moduleManifest{} | ||
90 | |||
91 | manifestPath := filepath.Join(s.StorageDir, manifestName) | ||
92 | data, err := ioutil.ReadFile(manifestPath) | ||
93 | if err != nil && !os.IsNotExist(err) { | ||
94 | return manifest, err | ||
95 | } | ||
96 | |||
97 | if len(data) == 0 { | ||
98 | return manifest, nil | ||
99 | } | ||
100 | |||
101 | if err := json.Unmarshal(data, &manifest); err != nil { | ||
102 | return manifest, err | ||
103 | } | ||
104 | |||
105 | for i, rec := range manifest.Modules { | ||
106 | // If the path was recorded before we changed to always using a | ||
107 | // slash as separator, we delete the record from the manifest so | ||
108 | // it can be discovered again and will be recorded using a slash. | ||
109 | if strings.Contains(rec.Dir, "\\") { | ||
110 | manifest.Modules[i] = manifest.Modules[len(manifest.Modules)-1] | ||
111 | manifest.Modules = manifest.Modules[:len(manifest.Modules)-1] | ||
112 | continue | ||
113 | } | ||
114 | |||
115 | // Make sure we use the correct path separator. | ||
116 | rec.Dir = filepath.FromSlash(rec.Dir) | ||
117 | } | ||
118 | |||
119 | return manifest, nil | ||
120 | } | ||
121 | |||
122 | // Store the location of the module, along with the version used and the module | ||
123 | // root directory. The storage method loads the entire file and rewrites it | ||
124 | // each time. This is only done a few times during init, so efficiency is | ||
125 | // not a concern. | ||
126 | func (s Storage) recordModule(rec moduleRecord) error { | ||
127 | manifest, err := s.loadManifest() | ||
128 | if err != nil { | ||
129 | // if there was a problem with the file, we will attempt to write a new | ||
130 | // one. Any non-data related error should surface there. | ||
131 | log.Printf("[WARN] error reading module manifest: %s", err) | ||
132 | } | ||
133 | |||
134 | // do nothing if we already have the exact module | ||
135 | for i, stored := range manifest.Modules { | ||
136 | if rec == stored { | ||
137 | return nil | ||
138 | } | ||
139 | |||
140 | // they are not equal, but if the storage path is the same we need to | ||
141 | // remove this rec to be replaced. | ||
142 | if rec.Dir == stored.Dir { | ||
143 | manifest.Modules[i] = manifest.Modules[len(manifest.Modules)-1] | ||
144 | manifest.Modules = manifest.Modules[:len(manifest.Modules)-1] | ||
145 | break | ||
146 | } | ||
147 | } | ||
148 | |||
149 | // Make sure we always use a slash separator. | ||
150 | rec.Dir = filepath.ToSlash(rec.Dir) | ||
151 | |||
152 | manifest.Modules = append(manifest.Modules, rec) | ||
153 | |||
154 | js, err := json.Marshal(manifest) | ||
155 | if err != nil { | ||
156 | panic(err) | ||
157 | } | ||
158 | |||
159 | manifestPath := filepath.Join(s.StorageDir, manifestName) | ||
160 | return ioutil.WriteFile(manifestPath, js, 0644) | ||
161 | } | ||
162 | |||
163 | // load the manifest from dir, and return all module versions matching the | ||
164 | // provided source. Records with no version info will be skipped, as they need | ||
165 | // to be uniquely identified by other means. | ||
166 | func (s Storage) moduleVersions(source string) ([]moduleRecord, error) { | ||
167 | manifest, err := s.loadManifest() | ||
168 | if err != nil { | ||
169 | return manifest.Modules, err | ||
170 | } | ||
171 | |||
172 | var matching []moduleRecord | ||
173 | |||
174 | for _, m := range manifest.Modules { | ||
175 | if m.Source == source && m.Version != "" { | ||
176 | log.Printf("[DEBUG] found local version %q for module %s", m.Version, m.Source) | ||
177 | matching = append(matching, m) | ||
178 | } | ||
179 | } | ||
180 | |||
181 | return matching, nil | ||
182 | } | ||
183 | |||
184 | func (s Storage) moduleDir(key string) (string, error) { | ||
185 | manifest, err := s.loadManifest() | ||
186 | if err != nil { | ||
187 | return "", err | ||
188 | } | ||
189 | |||
190 | for _, m := range manifest.Modules { | ||
191 | if m.Key == key { | ||
192 | return m.Dir, nil | ||
193 | } | ||
194 | } | ||
195 | |||
196 | return "", nil | ||
197 | } | ||
198 | |||
199 | // return only the root directory of the module stored in dir. | ||
200 | func (s Storage) getModuleRoot(dir string) (string, error) { | ||
201 | manifest, err := s.loadManifest() | ||
202 | if err != nil { | ||
203 | return "", err | ||
204 | } | ||
205 | |||
206 | for _, mod := range manifest.Modules { | ||
207 | if mod.Dir == dir { | ||
208 | return mod.Root, nil | ||
209 | } | ||
210 | } | ||
211 | return "", nil | ||
212 | } | ||
213 | |||
214 | // record only the Root directory for the module stored at dir. | ||
215 | func (s Storage) recordModuleRoot(dir, root string) error { | ||
216 | rec := moduleRecord{ | ||
217 | Dir: dir, | ||
218 | Root: root, | ||
219 | } | ||
220 | |||
221 | return s.recordModule(rec) | ||
222 | } | ||
223 | |||
224 | func (s Storage) output(msg string) { | ||
225 | if s.Ui == nil || s.Mode == GetModeNone { | ||
226 | return | ||
227 | } | ||
228 | s.Ui.Output(msg) | ||
229 | } | ||
230 | |||
231 | func (s Storage) getStorage(key string, src string) (string, bool, error) { | ||
232 | storage := &getter.FolderStorage{ | ||
233 | StorageDir: s.StorageDir, | ||
234 | } | ||
235 | |||
236 | log.Printf("[DEBUG] fetching module from %s", src) | ||
237 | |||
238 | // Get the module with the level specified if we were told to. | ||
239 | if s.Mode > GetModeNone { | ||
240 | log.Printf("[DEBUG] fetching %q with key %q", src, key) | ||
241 | if err := storage.Get(key, src, s.Mode == GetModeUpdate); err != nil { | ||
242 | return "", false, err | ||
243 | } | ||
244 | } | ||
245 | |||
246 | // Get the directory where the module is. | ||
247 | dir, found, err := storage.Dir(key) | ||
248 | log.Printf("[DEBUG] found %q in %q: %t", src, dir, found) | ||
249 | return dir, found, err | ||
250 | } | ||
251 | |||
252 | // find a stored module that's not from a registry | ||
253 | func (s Storage) findModule(key string) (string, error) { | ||
254 | if s.Mode == GetModeUpdate { | ||
255 | return "", nil | ||
256 | } | ||
257 | |||
258 | return s.moduleDir(key) | ||
259 | } | ||
260 | |||
261 | // GetModule fetches a module source into the specified directory. This is used | ||
262 | // as a convenience function by the CLI to initialize a configuration. | ||
263 | func (s Storage) GetModule(dst, src string) error { | ||
264 | // reset this in case the caller was going to re-use it | ||
265 | mode := s.Mode | ||
266 | s.Mode = GetModeUpdate | ||
267 | defer func() { | ||
268 | s.Mode = mode | ||
269 | }() | ||
270 | |||
271 | rec, err := s.findRegistryModule(src, anyVersion) | ||
272 | if err != nil { | ||
273 | return err | ||
274 | } | ||
275 | |||
276 | pwd, err := os.Getwd() | ||
277 | if err != nil { | ||
278 | return err | ||
279 | } | ||
280 | |||
281 | source := rec.url | ||
282 | if source == "" { | ||
283 | source, err = getter.Detect(src, pwd, getter.Detectors) | ||
284 | if err != nil { | ||
285 | return fmt.Errorf("module %s: %s", src, err) | ||
286 | } | ||
287 | } | ||
288 | |||
289 | if source == "" { | ||
290 | return fmt.Errorf("module %q not found", src) | ||
291 | } | ||
292 | |||
293 | return GetCopy(dst, source) | ||
294 | } | ||
295 | |||
296 | // find a registry module | ||
297 | func (s Storage) findRegistryModule(mSource, constraint string) (moduleRecord, error) { | ||
298 | rec := moduleRecord{ | ||
299 | Source: mSource, | ||
300 | } | ||
301 | // detect if we have a registry source | ||
302 | mod, err := regsrc.ParseModuleSource(mSource) | ||
303 | switch err { | ||
304 | case nil: | ||
305 | //ok | ||
306 | case regsrc.ErrInvalidModuleSource: | ||
307 | return rec, nil | ||
308 | default: | ||
309 | return rec, err | ||
310 | } | ||
311 | rec.registry = true | ||
312 | |||
313 | log.Printf("[TRACE] %q is a registry module", mod.Display()) | ||
314 | |||
315 | versions, err := s.moduleVersions(mod.String()) | ||
316 | if err != nil { | ||
317 | log.Printf("[ERROR] error looking up versions for %q: %s", mod.Display(), err) | ||
318 | return rec, err | ||
319 | } | ||
320 | |||
321 | match, err := newestRecord(versions, constraint) | ||
322 | if err != nil { | ||
323 | log.Printf("[INFO] no matching version for %q<%s>, %s", mod.Display(), constraint, err) | ||
324 | } | ||
325 | log.Printf("[DEBUG] matched %q version %s for %s", mod, match.Version, constraint) | ||
326 | |||
327 | rec.Dir = match.Dir | ||
328 | rec.Version = match.Version | ||
329 | found := rec.Dir != "" | ||
330 | |||
331 | // we need to lookup available versions | ||
332 | // Only on Get if it's not found, on unconditionally on Update | ||
333 | if (s.Mode == GetModeGet && !found) || (s.Mode == GetModeUpdate) { | ||
334 | resp, err := s.registry.Versions(mod) | ||
335 | if err != nil { | ||
336 | return rec, err | ||
337 | } | ||
338 | |||
339 | if len(resp.Modules) == 0 { | ||
340 | return rec, fmt.Errorf("module %q not found in registry", mod.Display()) | ||
341 | } | ||
342 | |||
343 | match, err := newestVersion(resp.Modules[0].Versions, constraint) | ||
344 | if err != nil { | ||
345 | return rec, err | ||
346 | } | ||
347 | |||
348 | if match == nil { | ||
349 | return rec, fmt.Errorf("no versions for %q found matching %q", mod.Display(), constraint) | ||
350 | } | ||
351 | |||
352 | rec.Version = match.Version | ||
353 | |||
354 | rec.url, err = s.registry.Location(mod, rec.Version) | ||
355 | if err != nil { | ||
356 | return rec, err | ||
357 | } | ||
358 | |||
359 | // we've already validated this by now | ||
360 | host, _ := mod.SvcHost() | ||
361 | s.output(fmt.Sprintf(" Found version %s of %s on %s", rec.Version, mod.Module(), host.ForDisplay())) | ||
362 | |||
363 | } | ||
364 | return rec, nil | ||
365 | } | ||