github.com/federicobaldo/terraform@v0.6.15-0.20160323222747-b20f680cbf05/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 }