github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/configs/configload/module_manifest.go (about) 1 package configload 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 version "github.com/hashicorp/go-version" 11 ) 12 13 // moduleRecord represents some metadata about an installed module, as part 14 // of a moduleManifest. 15 type moduleRecord struct { 16 // Key is a unique identifier for this particular module, based on its 17 // position within the static module tree. 18 Key string `json:"Key"` 19 20 // SourceAddr is the source address given for this module in configuration. 21 // This is used only to detect if the source was changed in configuration 22 // since the module was last installed, which means that the installer 23 // must re-install it. 24 SourceAddr string `json:"Source"` 25 26 // Version is the exact version of the module, which results from parsing 27 // VersionStr. nil for un-versioned modules. 28 Version *version.Version `json:"-"` 29 30 // VersionStr is the version specifier string. This is used only for 31 // serialization in snapshots and should not be accessed or updated 32 // by any other codepaths; use "Version" instead. 33 VersionStr string `json:"Version,omitempty"` 34 35 // Dir is the path to the local directory where the module is installed. 36 Dir string `json:"Dir"` 37 } 38 39 // moduleManifest is a map used to keep track of the filesystem locations 40 // and other metadata about installed modules. 41 // 42 // The configuration loader refers to this, while the module installer updates 43 // it to reflect any changes to the installed modules. 44 type moduleManifest map[string]moduleRecord 45 46 func manifestKey(path []string) string { 47 return strings.Join(path, ".") 48 } 49 50 // manifestSnapshotFile is an internal struct used only to assist in our JSON 51 // serializtion of manifest snapshots. It should not be used for any other 52 // purposes. 53 type manifestSnapshotFile struct { 54 Records []moduleRecord `json:"Modules"` 55 } 56 57 const manifestFilename = "modules.json" 58 59 func (m *moduleMgr) manifestSnapshotPath() string { 60 return filepath.Join(m.Dir, manifestFilename) 61 } 62 63 // readModuleManifestSnapshot loads a manifest snapshot from the filesystem. 64 func (m *moduleMgr) readModuleManifestSnapshot() error { 65 src, err := m.FS.ReadFile(m.manifestSnapshotPath()) 66 if err != nil { 67 if os.IsNotExist(err) { 68 // We'll treat a missing file as an empty manifest 69 m.manifest = make(moduleManifest) 70 return nil 71 } 72 return err 73 } 74 75 if len(src) == 0 { 76 // This should never happen, but we'll tolerate it as if it were 77 // a valid empty JSON object. 78 m.manifest = make(moduleManifest) 79 return nil 80 } 81 82 var read manifestSnapshotFile 83 err = json.Unmarshal(src, &read) 84 85 new := make(moduleManifest) 86 for _, record := range read.Records { 87 if record.VersionStr != "" { 88 record.Version, err = version.NewVersion(record.VersionStr) 89 if err != nil { 90 return fmt.Errorf("invalid version %q for %s: %s", record.VersionStr, record.Key, err) 91 } 92 } 93 if _, exists := new[record.Key]; exists { 94 // This should never happen in any valid file, so we'll catch it 95 // and report it to avoid confusing/undefined behavior if the 96 // snapshot file was edited incorrectly outside of Terraform. 97 return fmt.Errorf("snapshot file contains two records for path %s", record.Key) 98 } 99 new[record.Key] = record 100 } 101 102 m.manifest = new 103 104 return nil 105 } 106 107 // writeModuleManifestSnapshot writes a snapshot of the current manifest 108 // to the filesystem. 109 // 110 // The caller must guarantee no concurrent modifications of the manifest for 111 // the duration of a call to this function, or the behavior is undefined. 112 func (m *moduleMgr) writeModuleManifestSnapshot() error { 113 var write manifestSnapshotFile 114 115 for _, record := range m.manifest { 116 // Make sure VersionStr is in sync with Version, since we encourage 117 // callers to manipulate Version and ignore VersionStr. 118 if record.Version != nil { 119 record.VersionStr = record.Version.String() 120 } else { 121 record.VersionStr = "" 122 } 123 write.Records = append(write.Records, record) 124 } 125 126 src, err := json.Marshal(write) 127 if err != nil { 128 return err 129 } 130 131 return m.FS.WriteFile(m.manifestSnapshotPath(), src, os.ModePerm) 132 }