github.com/maheshbr/terraform@v0.3.1-0.20141020033300-deec7194a3ea/terraform/state_test.go (about) 1 package terraform 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "reflect" 7 "strings" 8 "testing" 9 10 "github.com/hashicorp/terraform/config" 11 ) 12 13 func TestStateAddModule(t *testing.T) { 14 cases := []struct { 15 In [][]string 16 Out [][]string 17 }{ 18 { 19 [][]string{ 20 []string{"root"}, 21 []string{"root", "child"}, 22 }, 23 [][]string{ 24 []string{"root"}, 25 []string{"root", "child"}, 26 }, 27 }, 28 29 { 30 [][]string{ 31 []string{"root", "foo", "bar"}, 32 []string{"root", "foo"}, 33 []string{"root"}, 34 []string{"root", "bar"}, 35 }, 36 [][]string{ 37 []string{"root"}, 38 []string{"root", "bar"}, 39 []string{"root", "foo"}, 40 []string{"root", "foo", "bar"}, 41 }, 42 }, 43 } 44 45 for _, tc := range cases { 46 s := new(State) 47 for _, p := range tc.In { 48 s.AddModule(p) 49 } 50 51 actual := make([][]string, 0, len(tc.In)) 52 for _, m := range s.Modules { 53 actual = append(actual, m.Path) 54 } 55 56 if !reflect.DeepEqual(actual, tc.Out) { 57 t.Fatalf("In: %#v\n\nOut: %#v", tc.In, actual) 58 } 59 } 60 } 61 62 func TestInstanceState_MergeDiff(t *testing.T) { 63 is := InstanceState{ 64 ID: "foo", 65 Attributes: map[string]string{ 66 "foo": "bar", 67 "port": "8000", 68 }, 69 } 70 71 diff := &InstanceDiff{ 72 Attributes: map[string]*ResourceAttrDiff{ 73 "foo": &ResourceAttrDiff{ 74 Old: "bar", 75 New: "baz", 76 }, 77 "bar": &ResourceAttrDiff{ 78 Old: "", 79 New: "foo", 80 }, 81 "baz": &ResourceAttrDiff{ 82 Old: "", 83 New: "foo", 84 NewComputed: true, 85 }, 86 "port": &ResourceAttrDiff{ 87 NewRemoved: true, 88 }, 89 }, 90 } 91 92 is2 := is.MergeDiff(diff) 93 94 expected := map[string]string{ 95 "foo": "baz", 96 "bar": "foo", 97 "baz": config.UnknownVariableValue, 98 } 99 100 if !reflect.DeepEqual(expected, is2.Attributes) { 101 t.Fatalf("bad: %#v", is2.Attributes) 102 } 103 } 104 105 func TestInstanceState_MergeDiff_nil(t *testing.T) { 106 var is *InstanceState = nil 107 108 diff := &InstanceDiff{ 109 Attributes: map[string]*ResourceAttrDiff{ 110 "foo": &ResourceAttrDiff{ 111 Old: "", 112 New: "baz", 113 }, 114 }, 115 } 116 117 is2 := is.MergeDiff(diff) 118 119 expected := map[string]string{ 120 "foo": "baz", 121 } 122 123 if !reflect.DeepEqual(expected, is2.Attributes) { 124 t.Fatalf("bad: %#v", is2.Attributes) 125 } 126 } 127 128 func TestInstanceState_MergeDiff_nilDiff(t *testing.T) { 129 is := InstanceState{ 130 ID: "foo", 131 Attributes: map[string]string{ 132 "foo": "bar", 133 }, 134 } 135 136 is2 := is.MergeDiff(nil) 137 138 expected := map[string]string{ 139 "foo": "bar", 140 } 141 142 if !reflect.DeepEqual(expected, is2.Attributes) { 143 t.Fatalf("bad: %#v", is2.Attributes) 144 } 145 } 146 147 func TestReadUpgradeState(t *testing.T) { 148 state := &StateV1{ 149 Resources: map[string]*ResourceStateV1{ 150 "foo": &ResourceStateV1{ 151 ID: "bar", 152 }, 153 }, 154 } 155 buf := new(bytes.Buffer) 156 if err := testWriteStateV1(state, buf); err != nil { 157 t.Fatalf("err: %s", err) 158 } 159 160 // ReadState should transparently detect the old 161 // version and upgrade up so the latest. 162 actual, err := ReadState(buf) 163 if err != nil { 164 t.Fatalf("err: %s", err) 165 } 166 167 upgraded, err := upgradeV1State(state) 168 if err != nil { 169 t.Fatalf("err: %s", err) 170 } 171 172 if !reflect.DeepEqual(actual, upgraded) { 173 t.Fatalf("bad: %#v", actual) 174 } 175 } 176 177 func TestReadWriteState(t *testing.T) { 178 state := &State{ 179 Serial: 9, 180 Modules: []*ModuleState{ 181 &ModuleState{ 182 Path: rootModulePath, 183 Resources: map[string]*ResourceState{ 184 "foo": &ResourceState{ 185 Primary: &InstanceState{ 186 ID: "bar", 187 Ephemeral: EphemeralState{ 188 ConnInfo: map[string]string{ 189 "type": "ssh", 190 "user": "root", 191 "password": "supersecret", 192 }, 193 }, 194 }, 195 }, 196 }, 197 }, 198 }, 199 } 200 201 // Checksum before the write 202 chksum := checksumStruct(t, state) 203 204 buf := new(bytes.Buffer) 205 if err := WriteState(state, buf); err != nil { 206 t.Fatalf("err: %s", err) 207 } 208 209 // Verify that the version and serial are set 210 if state.Version != textStateVersion { 211 t.Fatalf("bad version number: %d", state.Version) 212 } 213 214 // Verify the serial number is incremented 215 if state.Serial != 10 { 216 t.Fatalf("bad serial: %d", state.Serial) 217 } 218 219 // Remove the changes or the checksum will fail 220 state.Version = 0 221 state.Serial = 9 222 223 // Checksum after the write 224 chksumAfter := checksumStruct(t, state) 225 if chksumAfter != chksum { 226 t.Fatalf("structure changed during serialization!") 227 } 228 229 actual, err := ReadState(buf) 230 if err != nil { 231 t.Fatalf("err: %s", err) 232 } 233 234 // Verify the changes came through 235 state.Version = textStateVersion 236 state.Serial = 10 237 238 // ReadState should not restore sensitive information! 239 mod := state.RootModule() 240 mod.Resources["foo"].Primary.Ephemeral = EphemeralState{} 241 242 if !reflect.DeepEqual(actual, state) { 243 t.Fatalf("bad: %#v", actual) 244 } 245 } 246 247 func TestReadStateNewVersion(t *testing.T) { 248 type out struct { 249 Version int 250 } 251 252 buf, err := json.Marshal(&out{textStateVersion + 1}) 253 if err != nil { 254 t.Fatalf("err: %v", err) 255 } 256 257 s, err := ReadState(bytes.NewReader(buf)) 258 if s != nil { 259 t.Fatalf("unexpected: %#v", s) 260 } 261 if !strings.Contains(err.Error(), "not supported") { 262 t.Fatalf("err: %v", err) 263 } 264 } 265 266 func TestUpgradeV1State(t *testing.T) { 267 old := &StateV1{ 268 Outputs: map[string]string{ 269 "ip": "127.0.0.1", 270 }, 271 Resources: map[string]*ResourceStateV1{ 272 "foo": &ResourceStateV1{ 273 Type: "test_resource", 274 ID: "bar", 275 Attributes: map[string]string{ 276 "key": "val", 277 }, 278 }, 279 "bar": &ResourceStateV1{ 280 Type: "test_resource", 281 ID: "1234", 282 Attributes: map[string]string{ 283 "a": "b", 284 }, 285 }, 286 }, 287 Tainted: map[string]struct{}{ 288 "bar": struct{}{}, 289 }, 290 } 291 state, err := upgradeV1State(old) 292 if err != nil { 293 t.Fatalf("err: %v", err) 294 } 295 296 if len(state.Modules) != 1 { 297 t.Fatalf("should only have root module: %#v", state.Modules) 298 } 299 root := state.RootModule() 300 301 if len(root.Outputs) != 1 { 302 t.Fatalf("bad outputs: %v", root.Outputs) 303 } 304 if root.Outputs["ip"] != "127.0.0.1" { 305 t.Fatalf("bad outputs: %v", root.Outputs) 306 } 307 308 if len(root.Resources) != 2 { 309 t.Fatalf("bad resources: %v", root.Resources) 310 } 311 312 foo := root.Resources["foo"] 313 if foo.Type != "test_resource" { 314 t.Fatalf("bad: %#v", foo) 315 } 316 if foo.Primary == nil || foo.Primary.ID != "bar" || 317 foo.Primary.Attributes["key"] != "val" { 318 t.Fatalf("bad: %#v", foo) 319 } 320 if len(foo.Tainted) > 0 { 321 t.Fatalf("bad: %#v", foo) 322 } 323 324 bar := root.Resources["bar"] 325 if bar.Type != "test_resource" { 326 t.Fatalf("bad: %#v", bar) 327 } 328 if bar.Primary != nil { 329 t.Fatalf("bad: %#v", bar) 330 } 331 if len(bar.Tainted) != 1 { 332 t.Fatalf("bad: %#v", bar) 333 } 334 bt := bar.Tainted[0] 335 if bt.ID != "1234" || bt.Attributes["a"] != "b" { 336 t.Fatalf("bad: %#v", bt) 337 } 338 }