12 version "github.com/hashicorp/go-version"
14 "github.com/hashicorp/terraform/addrs"
17 // Record represents some metadata about an installed module, as part
18 // of a ModuleManifest.
20 // Key is a unique identifier for this particular module, based on its
21 // position within the static module tree.
22 Key string `json:"Key"`
24 // SourceAddr is the source address given for this module in configuration.
25 // This is used only to detect if the source was changed in configuration
26 // since the module was last installed, which means that the installer
27 // must re-install it.
28 SourceAddr string `json:"Source"`
30 // Version is the exact version of the module, which results from parsing
31 // VersionStr. nil for un-versioned modules.
32 Version *version.Version `json:"-"`
34 // VersionStr is the version specifier string. This is used only for
35 // serialization in snapshots and should not be accessed or updated
36 // by any other codepaths; use "Version" instead.
37 VersionStr string `json:"Version,omitempty"`
39 // Dir is the path to the local directory where the module is installed.
40 Dir string `json:"Dir"`
43 // Manifest is a map used to keep track of the filesystem locations
44 // and other metadata about installed modules.
46 // The configuration loader refers to this, while the module installer updates
47 // it to reflect any changes to the installed modules.
48 type Manifest map[string]Record
50 func (m Manifest) ModuleKey(path addrs.Module) string {
54 // manifestSnapshotFile is an internal struct used only to assist in our JSON
55 // serialization of manifest snapshots. It should not be used for any other
57 type manifestSnapshotFile struct {
58 Records []Record `json:"Modules"`
61 func ReadManifestSnapshot(r io.Reader) (Manifest, error) {
62 src, err := ioutil.ReadAll(r)
68 // This should never happen, but we'll tolerate it as if it were
69 // a valid empty JSON object.
70 return make(Manifest), nil
73 var read manifestSnapshotFile
74 err = json.Unmarshal(src, &read)
77 for _, record := range read.Records {
78 if record.VersionStr != "" {
79 record.Version, err = version.NewVersion(record.VersionStr)
81 return nil, fmt.Errorf("invalid version %q for %s: %s", record.VersionStr, record.Key, err)
84 if _, exists := new[record.Key]; exists {
85 // This should never happen in any valid file, so we'll catch it
86 // and report it to avoid confusing/undefined behavior if the
87 // snapshot file was edited incorrectly outside of Terraform.
88 return nil, fmt.Errorf("snapshot file contains two records for path %s", record.Key)
90 new[record.Key] = record
95 func ReadManifestSnapshotForDir(dir string) (Manifest, error) {
96 fn := filepath.Join(dir, ManifestSnapshotFilename)
99 if os.IsNotExist(err) {
100 return make(Manifest), nil // missing file is okay and treated as empty
104 return ReadManifestSnapshot(r)
107 func (m Manifest) WriteSnapshot(w io.Writer) error {
108 var write manifestSnapshotFile
110 for _, record := range m {
111 // Make sure VersionStr is in sync with Version, since we encourage
112 // callers to manipulate Version and ignore VersionStr.
113 if record.Version != nil {
114 record.VersionStr = record.Version.String()
116 record.VersionStr = ""
118 write.Records = append(write.Records, record)
121 src, err := json.Marshal(write)
126 _, err = w.Write(src)
130 func (m Manifest) WriteSnapshotToDir(dir string) error {
131 fn := filepath.Join(dir, ManifestSnapshotFilename)
132 log.Printf("[TRACE] modsdir: writing modules manifest to %s", fn)
133 w, err := os.Create(fn)
137 return m.WriteSnapshot(w)