internal/modsdir: Factor out module manifest models to separate package
This commit is contained in:
parent
8ca1fcec51
commit
0ce2add59f
|
@ -1,6 +1,10 @@
|
||||||
package configload
|
package configload
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/internal/modsdir"
|
||||||
"github.com/hashicorp/terraform/registry"
|
"github.com/hashicorp/terraform/registry"
|
||||||
"github.com/hashicorp/terraform/svchost/disco"
|
"github.com/hashicorp/terraform/svchost/disco"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
|
@ -34,5 +38,39 @@ type moduleMgr struct {
|
||||||
// after a set of updates are completed the installer must call
|
// after a set of updates are completed the installer must call
|
||||||
// writeModuleManifestSnapshot to persist a snapshot of the manifest
|
// writeModuleManifestSnapshot to persist a snapshot of the manifest
|
||||||
// to disk for use on subsequent runs.
|
// to disk for use on subsequent runs.
|
||||||
manifest moduleManifest
|
manifest modsdir.Manifest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *moduleMgr) manifestSnapshotPath() string {
|
||||||
|
return filepath.Join(m.Dir, modsdir.ManifestSnapshotFilename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readModuleManifestSnapshot loads a manifest snapshot from the filesystem.
|
||||||
|
func (m *moduleMgr) readModuleManifestSnapshot() error {
|
||||||
|
r, err := m.FS.Open(m.manifestSnapshotPath())
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// We'll treat a missing file as an empty manifest
|
||||||
|
m.manifest = make(modsdir.Manifest)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.manifest, err = modsdir.ReadManifestSnapshot(r)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeModuleManifestSnapshot writes a snapshot of the current manifest
|
||||||
|
// to the filesystem.
|
||||||
|
//
|
||||||
|
// The caller must guarantee no concurrent modifications of the manifest for
|
||||||
|
// the duration of a call to this function, or the behavior is undefined.
|
||||||
|
func (m *moduleMgr) writeModuleManifestSnapshot() error {
|
||||||
|
w, err := m.FS.Create(m.manifestSnapshotPath())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.manifest.WriteSnapshot(w)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Package modsdir is an internal package containing the model types used to
|
||||||
|
// represent the manifest of modules in a local modules cache directory.
|
||||||
|
package modsdir
|
|
@ -1,8 +1,10 @@
|
||||||
package configload
|
package modsdir
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
@ -11,9 +13,9 @@ import (
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// moduleRecord represents some metadata about an installed module, as part
|
// Record represents some metadata about an installed module, as part
|
||||||
// of a moduleManifest.
|
// of a ModuleManifest.
|
||||||
type moduleRecord struct {
|
type Record struct {
|
||||||
// Key is a unique identifier for this particular module, based on its
|
// Key is a unique identifier for this particular module, based on its
|
||||||
// position within the static module tree.
|
// position within the static module tree.
|
||||||
Key string `json:"Key"`
|
Key string `json:"Key"`
|
||||||
|
@ -37,83 +39,71 @@ type moduleRecord struct {
|
||||||
Dir string `json:"Dir"`
|
Dir string `json:"Dir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// moduleManifest is a map used to keep track of the filesystem locations
|
// Manifest is a map used to keep track of the filesystem locations
|
||||||
// and other metadata about installed modules.
|
// and other metadata about installed modules.
|
||||||
//
|
//
|
||||||
// The configuration loader refers to this, while the module installer updates
|
// The configuration loader refers to this, while the module installer updates
|
||||||
// it to reflect any changes to the installed modules.
|
// it to reflect any changes to the installed modules.
|
||||||
type moduleManifest map[string]moduleRecord
|
type Manifest map[string]Record
|
||||||
|
|
||||||
func manifestKey(path addrs.Module) string {
|
func (m Manifest) ModuleKey(path addrs.Module) string {
|
||||||
return path.String()
|
return path.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// manifestSnapshotFile is an internal struct used only to assist in our JSON
|
// manifestSnapshotFile is an internal struct used only to assist in our JSON
|
||||||
// serializtion of manifest snapshots. It should not be used for any other
|
// serialization of manifest snapshots. It should not be used for any other
|
||||||
// purposes.
|
// purpose.
|
||||||
type manifestSnapshotFile struct {
|
type manifestSnapshotFile struct {
|
||||||
Records []moduleRecord `json:"Modules"`
|
Records []Record `json:"Modules"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const manifestFilename = "modules.json"
|
func ReadManifestSnapshot(r io.Reader) (Manifest, error) {
|
||||||
|
src, err := ioutil.ReadAll(r)
|
||||||
func (m *moduleMgr) manifestSnapshotPath() string {
|
|
||||||
return filepath.Join(m.Dir, manifestFilename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// readModuleManifestSnapshot loads a manifest snapshot from the filesystem.
|
|
||||||
func (m *moduleMgr) readModuleManifestSnapshot() error {
|
|
||||||
src, err := m.FS.ReadFile(m.manifestSnapshotPath())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
return nil, err
|
||||||
// We'll treat a missing file as an empty manifest
|
|
||||||
m.manifest = make(moduleManifest)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(src) == 0 {
|
if len(src) == 0 {
|
||||||
// This should never happen, but we'll tolerate it as if it were
|
// This should never happen, but we'll tolerate it as if it were
|
||||||
// a valid empty JSON object.
|
// a valid empty JSON object.
|
||||||
m.manifest = make(moduleManifest)
|
return make(Manifest), nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var read manifestSnapshotFile
|
var read manifestSnapshotFile
|
||||||
err = json.Unmarshal(src, &read)
|
err = json.Unmarshal(src, &read)
|
||||||
|
|
||||||
new := make(moduleManifest)
|
new := make(Manifest)
|
||||||
for _, record := range read.Records {
|
for _, record := range read.Records {
|
||||||
if record.VersionStr != "" {
|
if record.VersionStr != "" {
|
||||||
record.Version, err = version.NewVersion(record.VersionStr)
|
record.Version, err = version.NewVersion(record.VersionStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid version %q for %s: %s", record.VersionStr, record.Key, err)
|
return nil, fmt.Errorf("invalid version %q for %s: %s", record.VersionStr, record.Key, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, exists := new[record.Key]; exists {
|
if _, exists := new[record.Key]; exists {
|
||||||
// This should never happen in any valid file, so we'll catch it
|
// This should never happen in any valid file, so we'll catch it
|
||||||
// and report it to avoid confusing/undefined behavior if the
|
// and report it to avoid confusing/undefined behavior if the
|
||||||
// snapshot file was edited incorrectly outside of Terraform.
|
// snapshot file was edited incorrectly outside of Terraform.
|
||||||
return fmt.Errorf("snapshot file contains two records for path %s", record.Key)
|
return nil, fmt.Errorf("snapshot file contains two records for path %s", record.Key)
|
||||||
}
|
}
|
||||||
new[record.Key] = record
|
new[record.Key] = record
|
||||||
}
|
}
|
||||||
|
return new, nil
|
||||||
m.manifest = new
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeModuleManifestSnapshot writes a snapshot of the current manifest
|
func ReadManifestSnapshotForDir(dir string) (Manifest, error) {
|
||||||
// to the filesystem.
|
fn := filepath.Join(dir, ManifestSnapshotFilename)
|
||||||
//
|
r, err := os.Open(fn)
|
||||||
// The caller must guarantee no concurrent modifications of the manifest for
|
if err != nil {
|
||||||
// the duration of a call to this function, or the behavior is undefined.
|
return nil, err
|
||||||
func (m *moduleMgr) writeModuleManifestSnapshot() error {
|
}
|
||||||
|
return ReadManifestSnapshot(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Manifest) WriteSnapshot(w io.Writer) error {
|
||||||
var write manifestSnapshotFile
|
var write manifestSnapshotFile
|
||||||
|
|
||||||
for _, record := range m.manifest {
|
for _, record := range m {
|
||||||
// Make sure VersionStr is in sync with Version, since we encourage
|
// Make sure VersionStr is in sync with Version, since we encourage
|
||||||
// callers to manipulate Version and ignore VersionStr.
|
// callers to manipulate Version and ignore VersionStr.
|
||||||
if record.Version != nil {
|
if record.Version != nil {
|
||||||
|
@ -129,5 +119,15 @@ func (m *moduleMgr) writeModuleManifestSnapshot() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.FS.WriteFile(m.manifestSnapshotPath(), src, os.ModePerm)
|
_, err = w.Write(src)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Manifest) WriteSnapshotToDir(dir string) error {
|
||||||
|
fn := filepath.Join(dir, ManifestSnapshotFilename)
|
||||||
|
w, err := os.Create(fn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return m.WriteSnapshot(w)
|
||||||
}
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package modsdir
|
||||||
|
|
||||||
|
const ManifestSnapshotFilename = "modules.json"
|
Loading…
Reference in New Issue