package terraform import ( "bytes" "reflect" "strings" "testing" "github.com/davecgh/go-spew/spew" ) // TestReadUpgradeStateV1toV3 tests the state upgrade process from the V1 state // to the current version, and needs editing each time. This means it tests the // entire pipeline of upgrades (which migrate version to version). func TestReadUpgradeStateV1toV3(t *testing.T) { // ReadState should transparently detect the old version but will upgrade // it on Write. actual, err := ReadState(strings.NewReader(testV1State)) if err != nil { t.Fatalf("err: %s", err) } buf := new(bytes.Buffer) if err := WriteState(actual, buf); err != nil { t.Fatalf("err: %s", err) } if actual.Version != 3 { t.Fatalf("bad: State version not incremented; is %d", actual.Version) } roundTripped, err := ReadState(buf) if err != nil { t.Fatalf("err: %s", err) } if !reflect.DeepEqual(actual, roundTripped) { t.Logf("actual:\n%#v", actual) t.Fatalf("roundTripped:\n%#v", roundTripped) } } func TestReadUpgradeStateV1toV3_outputs(t *testing.T) { // ReadState should transparently detect the old version but will upgrade // it on Write. actual, err := ReadState(strings.NewReader(testV1StateWithOutputs)) if err != nil { t.Fatalf("err: %s", err) } buf := new(bytes.Buffer) if err := WriteState(actual, buf); err != nil { t.Fatalf("err: %s", err) } if actual.Version != 3 { t.Fatalf("bad: State version not incremented; is %d", actual.Version) } roundTripped, err := ReadState(buf) if err != nil { t.Fatalf("err: %s", err) } if !reflect.DeepEqual(actual, roundTripped) { spew.Config.DisableMethods = true t.Fatalf("bad:\n%s\n\nround tripped:\n%s\n", spew.Sdump(actual), spew.Sdump(roundTripped)) spew.Config.DisableMethods = false } } // Upgrading the state should not lose empty module Outputs and Resources maps // during upgrade. The init for a new module initializes new maps, so we may not // be expecting to check for a nil map. func TestReadUpgradeStateV1toV3_emptyState(t *testing.T) { // ReadState should transparently detect the old version but will upgrade // it on Write. orig, err := ReadStateV1([]byte(testV1EmptyState)) if err != nil { t.Fatalf("err: %s", err) } stateV2, err := upgradeStateV1ToV2(orig) if err != nil { t.Fatalf("error attempting upgradeStateV1ToV2: %s", err) } for _, m := range stateV2.Modules { if m.Resources == nil { t.Fatal("V1 to V2 upgrade lost module.Resources") } if m.Outputs == nil { t.Fatal("V1 to V2 upgrade lost module.Outputs") } } stateV3, err := upgradeStateV2ToV3(stateV2) if err != nil { t.Fatalf("error attempting to upgradeStateV2ToV3: %s", err) } for _, m := range stateV3.Modules { if m.Resources == nil { t.Fatal("V2 to V3 upgrade lost module.Resources") } if m.Outputs == nil { t.Fatal("V2 to V3 upgrade lost module.Outputs") } } } const testV1EmptyState = `{ "version": 1, "serial": 0, "modules": [ { "path": [ "root" ], "outputs": {}, "resources": {} } ] } ` const testV1State = `{ "version": 1, "serial": 9, "remote": { "type": "http", "config": { "url": "http://my-cool-server.com/" } }, "modules": [ { "path": [ "root" ], "outputs": null, "resources": { "foo": { "type": "", "primary": { "id": "bar" } } }, "depends_on": [ "aws_instance.bar" ] } ] } ` const testV1StateWithOutputs = `{ "version": 1, "serial": 9, "remote": { "type": "http", "config": { "url": "http://my-cool-server.com/" } }, "modules": [ { "path": [ "root" ], "outputs": { "foo": "bar", "baz": "foo" }, "resources": { "foo": { "type": "", "primary": { "id": "bar" } } }, "depends_on": [ "aws_instance.bar" ] } ] } `