mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-26 08:51:02 -06:00
2ff4582be2
On Unix-derived systems a directory must be marked as "executable" in order to be accessible, so our previous mode of 0660 here was unsufficient and would cause a failure if it happened to be the installer that was creating the plugins directory for the first time here. Now we'll make it executable and readable for all but only writable by the same user/group. For consistency, we also make the selections file itself readable by everyone. In both cases, the umask we are run with may further constrain these modes.
115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
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(
|
|
filepath.Dir(lf.filename), 0775,
|
|
) // ignore error since WriteFile below will generate a better one anyway
|
|
return ioutil.WriteFile(lf.filename, buf, 0664)
|
|
}
|
|
|
|
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,
|
|
})
|
|
}
|