github.com/mikesimons/terraform@v0.6.13-0.20160304043642-f11448c69214/terraform/state_v1_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/gob"
     6  	"errors"
     7  	"io"
     8  	"reflect"
     9  	"sync"
    10  	"testing"
    11  
    12  	"github.com/mitchellh/hashstructure"
    13  )
    14  
    15  func TestReadWriteStateV1(t *testing.T) {
    16  	state := &StateV1{
    17  		Resources: map[string]*ResourceStateV1{
    18  			"foo": &ResourceStateV1{
    19  				ID: "bar",
    20  				ConnInfo: map[string]string{
    21  					"type":     "ssh",
    22  					"user":     "root",
    23  					"password": "supersecret",
    24  				},
    25  			},
    26  		},
    27  	}
    28  
    29  	// Checksum before the write
    30  	chksum, err := hashstructure.Hash(state, nil)
    31  	if err != nil {
    32  		t.Fatalf("hash: %s", err)
    33  	}
    34  
    35  	buf := new(bytes.Buffer)
    36  	if err := testWriteStateV1(state, buf); err != nil {
    37  		t.Fatalf("err: %s", err)
    38  	}
    39  
    40  	// Checksum after the write
    41  	chksumAfter, err := hashstructure.Hash(state, nil)
    42  	if err != nil {
    43  		t.Fatalf("hash: %s", err)
    44  	}
    45  
    46  	if chksumAfter != chksum {
    47  		t.Fatalf("structure changed during serialization!")
    48  	}
    49  
    50  	actual, err := ReadStateV1(buf)
    51  	if err != nil {
    52  		t.Fatalf("err: %s", err)
    53  	}
    54  
    55  	// ReadState should not restore sensitive information!
    56  	state.Resources["foo"].ConnInfo = nil
    57  
    58  	if !reflect.DeepEqual(actual, state) {
    59  		t.Fatalf("bad: %#v", actual)
    60  	}
    61  }
    62  
    63  // sensitiveState is used to store sensitive state information
    64  // that should not be serialized. This is only used temporarily
    65  // and is restored into the state.
    66  type sensitiveState struct {
    67  	ConnInfo map[string]map[string]string
    68  
    69  	once sync.Once
    70  }
    71  
    72  func (s *sensitiveState) init() {
    73  	s.once.Do(func() {
    74  		s.ConnInfo = make(map[string]map[string]string)
    75  	})
    76  }
    77  
    78  // testWriteStateV1 writes a state somewhere in a binary format.
    79  // Only for testing now
    80  func testWriteStateV1(d *StateV1, dst io.Writer) error {
    81  	// Write the magic bytes so we can determine the file format later
    82  	n, err := dst.Write([]byte(stateFormatMagic))
    83  	if err != nil {
    84  		return err
    85  	}
    86  	if n != len(stateFormatMagic) {
    87  		return errors.New("failed to write state format magic bytes")
    88  	}
    89  
    90  	// Write a version byte so we can iterate on version at some point
    91  	n, err = dst.Write([]byte{stateFormatVersion})
    92  	if err != nil {
    93  		return err
    94  	}
    95  	if n != 1 {
    96  		return errors.New("failed to write state version byte")
    97  	}
    98  
    99  	// Prevent sensitive information from being serialized
   100  	sensitive := &sensitiveState{}
   101  	sensitive.init()
   102  	for name, r := range d.Resources {
   103  		if r.ConnInfo != nil {
   104  			sensitive.ConnInfo[name] = r.ConnInfo
   105  			r.ConnInfo = nil
   106  		}
   107  	}
   108  
   109  	// Serialize the state
   110  	err = gob.NewEncoder(dst).Encode(d)
   111  
   112  	// Restore the state
   113  	for name, info := range sensitive.ConnInfo {
   114  		d.Resources[name].ConnInfo = info
   115  	}
   116  
   117  	return err
   118  }