github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/command/plugins_lock.go (about)

     1  package command
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"path/filepath"
    10  )
    11  
    12  func (m *Meta) providerPluginsLock() *pluginSHA256LockFile {
    13  	return &pluginSHA256LockFile{
    14  		Filename: filepath.Join(m.pluginDir(), "lock.json"),
    15  	}
    16  }
    17  
    18  type pluginSHA256LockFile struct {
    19  	Filename string
    20  }
    21  
    22  // Read loads the lock information from the file and returns it. If the file
    23  // cannot be read, an empty map is returned to indicate that _no_ providers
    24  // are acceptable, since the user must run "terraform init" to lock some
    25  // providers before a context can be created.
    26  func (pf *pluginSHA256LockFile) Read() map[string][]byte {
    27  	// Returning an empty map is different than nil because it causes
    28  	// us to reject all plugins as uninitialized, rather than applying no
    29  	// constraints at all.
    30  	//
    31  	// We don't surface any specific errors here because we want it to all
    32  	// roll up into our more-user-friendly error that appears when plugin
    33  	// constraint verification fails during context creation.
    34  	digests := make(map[string][]byte)
    35  
    36  	buf, err := ioutil.ReadFile(pf.Filename)
    37  	if err != nil {
    38  		// This is expected if the user runs any context-using command before
    39  		// running "terraform init".
    40  		log.Printf("[INFO] Failed to read plugin lock file %s: %s", pf.Filename, err)
    41  		return digests
    42  	}
    43  
    44  	var strDigests map[string]string
    45  	err = json.Unmarshal(buf, &strDigests)
    46  	if err != nil {
    47  		// This should never happen unless the user directly edits the file.
    48  		log.Printf("[WARN] Plugin lock file %s failed to parse as JSON: %s", pf.Filename, err)
    49  		return digests
    50  	}
    51  
    52  	for name, strDigest := range strDigests {
    53  		var digest []byte
    54  		_, err := fmt.Sscanf(strDigest, "%x", &digest)
    55  		if err == nil {
    56  			digests[name] = digest
    57  		} else {
    58  			// This should never happen unless the user directly edits the file.
    59  			log.Printf("[WARN] Plugin lock file %s has invalid digest for %q", pf.Filename, name)
    60  		}
    61  	}
    62  
    63  	return digests
    64  }
    65  
    66  // Write persists lock information to disk, where it will be retrieved by
    67  // future calls to Read. This entirely replaces any previous lock information,
    68  // so the given map must be comprehensive.
    69  func (pf *pluginSHA256LockFile) Write(digests map[string][]byte) error {
    70  	strDigests := map[string]string{}
    71  	for name, digest := range digests {
    72  		strDigests[name] = fmt.Sprintf("%x", digest)
    73  	}
    74  
    75  	buf, err := json.MarshalIndent(strDigests, "", "  ")
    76  	if err != nil {
    77  		// should never happen
    78  		return fmt.Errorf("failed to serialize plugin lock as JSON: %s", err)
    79  	}
    80  
    81  	os.MkdirAll(
    82  		filepath.Dir(pf.Filename), os.ModePerm,
    83  	) // ignore error since WriteFile below will generate a better one anyway
    84  
    85  	return ioutil.WriteFile(pf.Filename, buf, os.ModePerm)
    86  }