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