core: Add test for V2->V3 state upgrade

This commit is contained in:
James Nugent 2016-06-09 10:44:17 +01:00
parent 706ccb7dfe
commit 9554d54116
3 changed files with 336 additions and 122 deletions

View File

@ -7,7 +7,6 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/davecgh/go-spew/spew"
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
) )
@ -1128,62 +1127,6 @@ func TestInstanceState_MergeDiff_nilDiff(t *testing.T) {
} }
} }
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.Fatalf("bad: %#v", actual)
}
}
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
}
}
func TestReadWriteState(t *testing.T) { func TestReadWriteState(t *testing.T) {
state := &State{ state := &State{
Serial: 9, Serial: 9,
@ -1420,68 +1363,3 @@ func TestParseResourceStateKey(t *testing.T) {
} }
} }
} }
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"
]
}
]
}
`

View File

@ -0,0 +1,134 @@
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.Fatalf("bad: %#v", actual)
}
}
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
}
}
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"
]
}
]
}
`

View File

@ -0,0 +1,202 @@
package terraform
import (
"bytes"
"strings"
"testing"
)
// TestReadUpgradeStateV2toV3 tests the state upgrade process from the V2 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 TestReadUpgradeStateV2toV3(t *testing.T) {
// ReadState should transparently detect the old version but will upgrade
// it on Write.
upgraded, err := ReadState(strings.NewReader(testV2State))
if err != nil {
t.Fatalf("err: %s", err)
}
buf := new(bytes.Buffer)
if err := WriteState(upgraded, buf); err != nil {
t.Fatalf("err: %s", err)
}
if upgraded.Version != 3 {
t.Fatalf("bad: State version not incremented; is %d", upgraded.Version)
}
// For this test we cannot assert that we match the round trip because an
// empty map has been removed from state. Instead we make assertions against
// some of the key fields in the _upgraded_ state.
instanceState, ok := upgraded.RootModule().Resources["test_resource.main"]
if !ok {
t.Fatalf("Instance state for test_resource.main was removed from state during upgrade")
}
primary := instanceState.Primary
if primary == nil {
t.Fatalf("Primary instance was removed from state for test_resource.main")
}
// Non-empty computed map is moved from .# to .%
if _, ok := primary.Attributes["computed_map.#"]; ok {
t.Fatalf("Count was not upgraded from .# to .%% for computed_map")
}
if count, ok := primary.Attributes["computed_map.%"]; !ok || count != "1" {
t.Fatalf("Count was not in .%% or was not 2 for computed_map")
}
// list_of_map top level retains .#
if count, ok := primary.Attributes["list_of_map.#"]; !ok || count != "2" {
t.Fatal("Count for list_of_map was migrated incorrectly")
}
// list_of_map.0 is moved from .# to .%
if _, ok := primary.Attributes["list_of_map.0.#"]; ok {
t.Fatalf("Count was not upgraded from .# to .%% for list_of_map.0")
}
if count, ok := primary.Attributes["list_of_map.0.%"]; !ok || count != "2" {
t.Fatalf("Count was not in .%% or was not 2 for list_of_map.0")
}
// list_of_map.1 is moved from .# to .%
if _, ok := primary.Attributes["list_of_map.1.#"]; ok {
t.Fatalf("Count was not upgraded from .# to .%% for list_of_map.1")
}
if count, ok := primary.Attributes["list_of_map.1.%"]; !ok || count != "2" {
t.Fatalf("Count was not in .%% or was not 2 for list_of_map.1")
}
// map is moved from .# to .%
if _, ok := primary.Attributes["map.#"]; ok {
t.Fatalf("Count was not upgraded from .# to .%% for map")
}
if count, ok := primary.Attributes["map.%"]; !ok || count != "2" {
t.Fatalf("Count was not in .%% or was not 2 for map")
}
// optional_computed_map should be removed from state
if _, ok := primary.Attributes["optional_computed_map"]; ok {
t.Fatal("optional_computed_map was not removed from state")
}
// required_map is moved from .# to .%
if _, ok := primary.Attributes["required_map.#"]; ok {
t.Fatalf("Count was not upgraded from .# to .%% for required_map")
}
if count, ok := primary.Attributes["required_map.%"]; !ok || count != "3" {
t.Fatalf("Count was not in .%% or was not 3 for map")
}
// computed_list keeps .#
if count, ok := primary.Attributes["computed_list.#"]; !ok || count != "2" {
t.Fatal("Count was migrated incorrectly for computed_list")
}
// computed_set keeps .#
if count, ok := primary.Attributes["computed_set.#"]; !ok || count != "2" {
t.Fatal("Count was migrated incorrectly for computed_set")
}
if val, ok := primary.Attributes["computed_set.2337322984"]; !ok || val != "setval1" {
t.Fatal("Set item for computed_set.2337322984 changed or moved")
}
if val, ok := primary.Attributes["computed_set.307881554"]; !ok || val != "setval2" {
t.Fatal("Set item for computed_set.307881554 changed or moved")
}
// string properties are unaffected
if val, ok := primary.Attributes["id"]; !ok || val != "testId" {
t.Fatal("id was not set correctly after migration")
}
}
const testV2State = `{
"version": 2,
"terraform_version": "0.7.0",
"serial": 2,
"modules": [
{
"path": [
"root"
],
"outputs": {
"computed_map": {
"sensitive": false,
"type": "map",
"value": {
"key1": "value1"
}
},
"computed_set": {
"sensitive": false,
"type": "list",
"value": [
"setval1",
"setval2"
]
},
"map": {
"sensitive": false,
"type": "map",
"value": {
"key": "test",
"test": "test"
}
},
"set": {
"sensitive": false,
"type": "list",
"value": [
"test1",
"test2"
]
}
},
"resources": {
"test_resource.main": {
"type": "test_resource",
"primary": {
"id": "testId",
"attributes": {
"computed_list.#": "2",
"computed_list.0": "listval1",
"computed_list.1": "listval2",
"computed_map.#": "1",
"computed_map.key1": "value1",
"computed_read_only": "value_from_api",
"computed_read_only_force_new": "value_from_api",
"computed_set.#": "2",
"computed_set.2337322984": "setval1",
"computed_set.307881554": "setval2",
"id": "testId",
"list_of_map.#": "2",
"list_of_map.0.#": "2",
"list_of_map.0.key1": "value1",
"list_of_map.0.key2": "value2",
"list_of_map.1.#": "2",
"list_of_map.1.key3": "value3",
"list_of_map.1.key4": "value4",
"map.#": "2",
"map.key": "test",
"map.test": "test",
"map_that_look_like_set.#": "2",
"map_that_look_like_set.12352223": "hello",
"map_that_look_like_set.36234341": "world",
"optional_computed_map.#": "0",
"required": "Hello World",
"required_map.#": "3",
"required_map.key1": "value1",
"required_map.key2": "value2",
"required_map.key3": "value3",
"set.#": "2",
"set.2326977762": "test1",
"set.331058520": "test2"
}
}
}
}
}
]
}
`