github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/legacy/terraform/upgrade_state_v1_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package terraform 5 6 import ( 7 "bytes" 8 "reflect" 9 "strings" 10 "testing" 11 12 "github.com/davecgh/go-spew/spew" 13 ) 14 15 // TestReadUpgradeStateV1toV3 tests the state upgrade process from the V1 state 16 // to the current version, and needs editing each time. This means it tests the 17 // entire pipeline of upgrades (which migrate version to version). 18 func TestReadUpgradeStateV1toV3(t *testing.T) { 19 // ReadState should transparently detect the old version but will upgrade 20 // it on Write. 21 actual, err := ReadState(strings.NewReader(testV1State)) 22 if err != nil { 23 t.Fatalf("err: %s", err) 24 } 25 26 buf := new(bytes.Buffer) 27 if err := WriteState(actual, buf); err != nil { 28 t.Fatalf("err: %s", err) 29 } 30 31 if actual.Version != 3 { 32 t.Fatalf("bad: State version not incremented; is %d", actual.Version) 33 } 34 35 roundTripped, err := ReadState(buf) 36 if err != nil { 37 t.Fatalf("err: %s", err) 38 } 39 40 if !reflect.DeepEqual(actual, roundTripped) { 41 t.Logf("actual:\n%#v", actual) 42 t.Fatalf("roundTripped:\n%#v", roundTripped) 43 } 44 } 45 46 func TestReadUpgradeStateV1toV3_outputs(t *testing.T) { 47 // ReadState should transparently detect the old version but will upgrade 48 // it on Write. 49 actual, err := ReadState(strings.NewReader(testV1StateWithOutputs)) 50 if err != nil { 51 t.Fatalf("err: %s", err) 52 } 53 54 buf := new(bytes.Buffer) 55 if err := WriteState(actual, buf); err != nil { 56 t.Fatalf("err: %s", err) 57 } 58 59 if actual.Version != 3 { 60 t.Fatalf("bad: State version not incremented; is %d", actual.Version) 61 } 62 63 roundTripped, err := ReadState(buf) 64 if err != nil { 65 t.Fatalf("err: %s", err) 66 } 67 68 if !reflect.DeepEqual(actual, roundTripped) { 69 spew.Config.DisableMethods = true 70 t.Fatalf("bad:\n%s\n\nround tripped:\n%s\n", spew.Sdump(actual), spew.Sdump(roundTripped)) 71 spew.Config.DisableMethods = false 72 } 73 } 74 75 // Upgrading the state should not lose empty module Outputs and Resources maps 76 // during upgrade. The init for a new module initializes new maps, so we may not 77 // be expecting to check for a nil map. 78 func TestReadUpgradeStateV1toV3_emptyState(t *testing.T) { 79 // ReadState should transparently detect the old version but will upgrade 80 // it on Write. 81 orig, err := ReadStateV1([]byte(testV1EmptyState)) 82 if err != nil { 83 t.Fatalf("err: %s", err) 84 } 85 86 stateV2, err := upgradeStateV1ToV2(orig) 87 if err != nil { 88 t.Fatalf("error attempting upgradeStateV1ToV2: %s", err) 89 } 90 91 for _, m := range stateV2.Modules { 92 if m.Resources == nil { 93 t.Fatal("V1 to V2 upgrade lost module.Resources") 94 } 95 if m.Outputs == nil { 96 t.Fatal("V1 to V2 upgrade lost module.Outputs") 97 } 98 } 99 100 stateV3, err := upgradeStateV2ToV3(stateV2) 101 if err != nil { 102 t.Fatalf("error attempting to upgradeStateV2ToV3: %s", err) 103 } 104 for _, m := range stateV3.Modules { 105 if m.Resources == nil { 106 t.Fatal("V2 to V3 upgrade lost module.Resources") 107 } 108 if m.Outputs == nil { 109 t.Fatal("V2 to V3 upgrade lost module.Outputs") 110 } 111 } 112 113 } 114 115 const testV1EmptyState = `{ 116 "version": 1, 117 "serial": 0, 118 "modules": [ 119 { 120 "path": [ 121 "root" 122 ], 123 "outputs": {}, 124 "resources": {} 125 } 126 ] 127 } 128 ` 129 130 const testV1State = `{ 131 "version": 1, 132 "serial": 9, 133 "remote": { 134 "type": "http", 135 "config": { 136 "url": "http://my-cool-server.com/" 137 } 138 }, 139 "modules": [ 140 { 141 "path": [ 142 "root" 143 ], 144 "outputs": null, 145 "resources": { 146 "foo": { 147 "type": "", 148 "primary": { 149 "id": "bar" 150 } 151 } 152 }, 153 "depends_on": [ 154 "aws_instance.bar" 155 ] 156 } 157 ] 158 } 159 ` 160 161 const testV1StateWithOutputs = `{ 162 "version": 1, 163 "serial": 9, 164 "remote": { 165 "type": "http", 166 "config": { 167 "url": "http://my-cool-server.com/" 168 } 169 }, 170 "modules": [ 171 { 172 "path": [ 173 "root" 174 ], 175 "outputs": { 176 "foo": "bar", 177 "baz": "foo" 178 }, 179 "resources": { 180 "foo": { 181 "type": "", 182 "primary": { 183 "id": "bar" 184 } 185 } 186 }, 187 "depends_on": [ 188 "aws_instance.bar" 189 ] 190 } 191 ] 192 } 193 `