github.com/medzin/terraform@v0.11.11/config/module/storage.go (about) 1 package module 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "os" 9 "path/filepath" 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 18 const manifestName = "modules.json" 19 20 // moduleManifest is the serialization structure used to record the stored 21 // module's metadata. 22 type 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. 29 type 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. 62 type 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. 77 func 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. 87 func (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 } 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. 110 func (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 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. 147 func (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 165 func (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. 181 func (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. 196 func (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 205 func (s Storage) output(msg string) { 206 if s.Ui == nil || s.Mode == GetModeNone { 207 return 208 } 209 s.Ui.Output(msg) 210 } 211 212 func (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 234 func (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. 244 func (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 278 func (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) { 315 resp, err := s.registry.Versions(mod) 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 335 rec.url, err = s.registry.Location(mod, rec.Version) 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 }