aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/config/module/storage.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/module/storage.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/config/module/storage.go365
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 @@
1package module
2
3import (
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
19const manifestName = "modules.json"
20
21// moduleManifest is the serialization structure used to record the stored
22// module's metadata.
23type 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.
30type 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.
63type 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.
78func 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.
88func (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.
126func (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.
166func (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
184func (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.
200func (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.
215func (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
224func (s Storage) output(msg string) {
225 if s.Ui == nil || s.Mode == GetModeNone {
226 return
227 }
228 s.Ui.Output(msg)
229}
230
231func (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
253func (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.
263func (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
297func (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}