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