package command import ( "encoding/json" "fmt" "io/ioutil" "log" "os" "path/filepath" ) func (m *Meta) providerPluginsLock() *pluginSHA256LockFile { return &pluginSHA256LockFile{ Filename: filepath.Join(m.pluginDir(), "lock.json"), } } type pluginSHA256LockFile struct { Filename string } // Read loads the lock information from the file and returns it. If the file // cannot be read, an empty map is returned to indicate that _no_ providers // are acceptable, since the user must run "terraform init" to lock some // providers before a context can be created. func (pf *pluginSHA256LockFile) Read() map[string][]byte { // Returning an empty map is different than nil because it causes // us to reject all plugins as uninitialized, rather than applying no // constraints at all. // // We don't surface any specific errors here because we want it to all // roll up into our more-user-friendly error that appears when plugin // constraint verification fails during context creation. digests := make(map[string][]byte) buf, err := ioutil.ReadFile(pf.Filename) if err != nil { // This is expected if the user runs any context-using command before // running "terraform init". log.Printf("[INFO] Failed to read plugin lock file %s: %s", pf.Filename, err) return digests } var strDigests map[string]string err = json.Unmarshal(buf, &strDigests) if err != nil { // This should never happen unless the user directly edits the file. log.Printf("[WARN] Plugin lock file %s failed to parse as JSON: %s", pf.Filename, err) return digests } for name, strDigest := range strDigests { var digest []byte _, err := fmt.Sscanf(strDigest, "%x", &digest) if err == nil { digests[name] = digest } else { // This should never happen unless the user directly edits the file. log.Printf("[WARN] Plugin lock file %s has invalid digest for %q", pf.Filename, name) } } return digests } // Write persists lock information to disk, where it will be retrieved by // future calls to Read. This entirely replaces any previous lock information, // so the given map must be comprehensive. func (pf *pluginSHA256LockFile) Write(digests map[string][]byte) error { strDigests := map[string]string{} for name, digest := range digests { strDigests[name] = fmt.Sprintf("%x", digest) } buf, err := json.MarshalIndent(strDigests, "", " ") if err != nil { // should never happen return fmt.Errorf("failed to serialize plugin lock as JSON: %s", err) } os.MkdirAll( filepath.Dir(pf.Filename), os.ModePerm, ) // ignore error since WriteFile below will generate a better one anyway return ioutil.WriteFile(pf.Filename, buf, os.ModePerm) }