2020-03-28 00:50:04 +01:00
|
|
|
package providercache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
|
|
"github.com/hashicorp/terraform/internal/getproviders"
|
|
|
|
)
|
|
|
|
|
|
|
|
// lockFile represents a file on disk that captures selected versions and
|
|
|
|
// their associated package checksums resulting from an install process, so
|
|
|
|
// that later consumers of that install process can be sure they are reading
|
|
|
|
// an identical set of providers to what the install process intended.
|
|
|
|
//
|
|
|
|
// This is an internal type used to encapsulate the reading, parsing,
|
|
|
|
// serializing, and writing of lock files. Its public interface is via methods
|
|
|
|
// on type Installer.
|
|
|
|
type lockFile struct {
|
|
|
|
filename string
|
|
|
|
}
|
|
|
|
|
|
|
|
// LockFileEntry represents an entry for a specific provider in a LockFile.
|
|
|
|
type lockFileEntry struct {
|
|
|
|
SelectedVersion getproviders.Version
|
|
|
|
PackageHash string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ json.Marshaler = (*lockFileEntry)(nil)
|
|
|
|
var _ json.Unmarshaler = (*lockFileEntry)(nil)
|
|
|
|
|
|
|
|
// Read returns the current locks captured in the lock file.
|
|
|
|
//
|
|
|
|
// If the file does not exist, the result is successful but empty to indicate
|
|
|
|
// that no providers at all are available for use.
|
|
|
|
func (lf *lockFile) Read() (map[addrs.Provider]lockFileEntry, error) {
|
|
|
|
buf, err := ioutil.ReadFile(lf.filename)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil, nil // no file means no locks yet
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var rawEntries map[string]*lockFileEntry
|
|
|
|
err = json.Unmarshal(buf, &rawEntries)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error parsing %s: %s", lf.filename, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret := make(map[addrs.Provider]lockFileEntry, len(rawEntries))
|
|
|
|
for providerStr, entry := range rawEntries {
|
|
|
|
provider, diags := addrs.ParseProviderSourceString(providerStr)
|
|
|
|
if diags.HasErrors() {
|
|
|
|
// This file is both generated and consumed by Terraform, so we
|
|
|
|
// don't use super-detailed error messages for problems in it.
|
|
|
|
// If we get here without someone tampering with the file then
|
|
|
|
// it's presumably a bug in either our serializer or our parser.
|
|
|
|
return nil, fmt.Errorf("error parsing %s: invalid provider address %q", lf.filename, providerStr)
|
|
|
|
}
|
|
|
|
ret[provider] = *entry
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write stores a new set of entries in the lock file, disarding any
|
|
|
|
// selections previously stored there.
|
|
|
|
func (lf *lockFile) Write(new map[addrs.Provider]lockFileEntry) error {
|
|
|
|
toStore := make(map[string]*lockFileEntry, len(new))
|
|
|
|
for provider := range new {
|
|
|
|
entry := new[provider] // so that each reference below is to a different object
|
|
|
|
toStore[provider.String()] = &entry
|
|
|
|
}
|
|
|
|
|
|
|
|
buf, err := json.MarshalIndent(toStore, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error writing %s: %s", lf.filename, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
os.MkdirAll(
|
2020-03-31 23:38:26 +02:00
|
|
|
filepath.Dir(lf.filename), 0775,
|
2020-03-28 00:50:04 +01:00
|
|
|
) // ignore error since WriteFile below will generate a better one anyway
|
2020-03-31 23:38:26 +02:00
|
|
|
return ioutil.WriteFile(lf.filename, buf, 0664)
|
2020-03-28 00:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (lfe *lockFileEntry) UnmarshalJSON(src []byte) error {
|
|
|
|
type Raw struct {
|
|
|
|
VersionStr string `json:"version"`
|
|
|
|
Hash string `json:"hash"`
|
|
|
|
}
|
|
|
|
var raw Raw
|
|
|
|
err := json.Unmarshal(src, &raw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
version, err := getproviders.ParseVersion(raw.VersionStr)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid version number: %s", err)
|
|
|
|
}
|
|
|
|
lfe.SelectedVersion = version
|
|
|
|
lfe.PackageHash = raw.Hash
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lfe *lockFileEntry) MarshalJSON() ([]byte, error) {
|
|
|
|
return json.Marshal(map[string]interface{}{
|
|
|
|
"version": lfe.SelectedVersion.String(),
|
|
|
|
"hash": lfe.PackageHash,
|
|
|
|
})
|
|
|
|
}
|