github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/command/apply_destroy_test.go (about) 1 package command 2 3 import ( 4 "os" 5 "strings" 6 "testing" 7 8 "github.com/davecgh/go-spew/spew" 9 "github.com/mitchellh/cli" 10 "github.com/zclconf/go-cty/cty" 11 12 "github.com/hashicorp/terraform/addrs" 13 "github.com/hashicorp/terraform/configs/configschema" 14 "github.com/hashicorp/terraform/providers" 15 "github.com/hashicorp/terraform/states" 16 "github.com/hashicorp/terraform/states/statefile" 17 "github.com/hashicorp/terraform/terraform" 18 ) 19 20 func TestApply_destroy(t *testing.T) { 21 originalState := states.BuildState(func(s *states.SyncState) { 22 s.SetResourceInstanceCurrent( 23 addrs.Resource{ 24 Mode: addrs.ManagedResourceMode, 25 Type: "test_instance", 26 Name: "foo", 27 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 28 &states.ResourceInstanceObjectSrc{ 29 AttrsJSON: []byte(`{"id":"bar"}`), 30 Status: states.ObjectReady, 31 }, 32 addrs.ProviderConfig{Type: addrs.NewLegacyProvider("test")}.Absolute(addrs.RootModuleInstance), 33 ) 34 }) 35 statePath := testStateFile(t, originalState) 36 37 p := testProvider() 38 p.GetSchemaReturn = &terraform.ProviderSchema{ 39 ResourceTypes: map[string]*configschema.Block{ 40 "test_instance": { 41 Attributes: map[string]*configschema.Attribute{ 42 "id": {Type: cty.String, Computed: true}, 43 "ami": {Type: cty.String, Optional: true}, 44 }, 45 }, 46 }, 47 } 48 49 ui := new(cli.MockUi) 50 c := &ApplyCommand{ 51 Destroy: true, 52 Meta: Meta{ 53 testingOverrides: metaOverridesForProvider(p), 54 Ui: ui, 55 }, 56 } 57 58 // Run the apply command pointing to our existing state 59 args := []string{ 60 "-auto-approve", 61 "-state", statePath, 62 testFixturePath("apply"), 63 } 64 if code := c.Run(args); code != 0 { 65 t.Log(ui.OutputWriter.String()) 66 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 67 } 68 69 // Verify a new state exists 70 if _, err := os.Stat(statePath); err != nil { 71 t.Fatalf("err: %s", err) 72 } 73 74 f, err := os.Open(statePath) 75 if err != nil { 76 t.Fatalf("err: %s", err) 77 } 78 defer f.Close() 79 80 stateFile, err := statefile.Read(f) 81 if err != nil { 82 t.Fatalf("err: %s", err) 83 } 84 if stateFile.State == nil { 85 t.Fatal("state should not be nil") 86 } 87 88 actualStr := strings.TrimSpace(stateFile.State.String()) 89 expectedStr := strings.TrimSpace(testApplyDestroyStr) 90 if actualStr != expectedStr { 91 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 92 } 93 94 // Should have a backup file 95 f, err = os.Open(statePath + DefaultBackupExtension) 96 if err != nil { 97 t.Fatalf("err: %s", err) 98 } 99 100 backupStateFile, err := statefile.Read(f) 101 f.Close() 102 if err != nil { 103 t.Fatalf("err: %s", err) 104 } 105 106 actualStr = strings.TrimSpace(backupStateFile.State.String()) 107 expectedStr = strings.TrimSpace(originalState.String()) 108 if actualStr != expectedStr { 109 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 110 } 111 } 112 113 func TestApply_destroyLockedState(t *testing.T) { 114 originalState := states.BuildState(func(s *states.SyncState) { 115 s.SetResourceInstanceCurrent( 116 addrs.Resource{ 117 Mode: addrs.ManagedResourceMode, 118 Type: "test_instance", 119 Name: "foo", 120 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 121 &states.ResourceInstanceObjectSrc{ 122 AttrsJSON: []byte(`{"id":"bar"}`), 123 Status: states.ObjectReady, 124 }, 125 addrs.ProviderConfig{Type: addrs.NewLegacyProvider("test")}.Absolute(addrs.RootModuleInstance), 126 ) 127 }) 128 statePath := testStateFile(t, originalState) 129 130 unlock, err := testLockState(testDataDir, statePath) 131 if err != nil { 132 t.Fatal(err) 133 } 134 defer unlock() 135 136 p := testProvider() 137 ui := new(cli.MockUi) 138 c := &ApplyCommand{ 139 Destroy: true, 140 Meta: Meta{ 141 testingOverrides: metaOverridesForProvider(p), 142 Ui: ui, 143 }, 144 } 145 146 // Run the apply command pointing to our existing state 147 args := []string{ 148 "-auto-approve", 149 "-state", statePath, 150 testFixturePath("apply"), 151 } 152 153 if code := c.Run(args); code == 0 { 154 t.Fatal("expected error") 155 } 156 157 output := ui.ErrorWriter.String() 158 if !strings.Contains(output, "lock") { 159 t.Fatal("command output does not look like a lock error:", output) 160 } 161 } 162 163 func TestApply_destroyPlan(t *testing.T) { 164 planPath := testPlanFileNoop(t) 165 166 p := testProvider() 167 ui := new(cli.MockUi) 168 c := &ApplyCommand{ 169 Destroy: true, 170 Meta: Meta{ 171 testingOverrides: metaOverridesForProvider(p), 172 Ui: ui, 173 }, 174 } 175 176 // Run the apply command pointing to our existing state 177 args := []string{ 178 planPath, 179 } 180 if code := c.Run(args); code != 1 { 181 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 182 } 183 } 184 185 func TestApply_destroyTargeted(t *testing.T) { 186 originalState := states.BuildState(func(s *states.SyncState) { 187 s.SetResourceInstanceCurrent( 188 addrs.Resource{ 189 Mode: addrs.ManagedResourceMode, 190 Type: "test_instance", 191 Name: "foo", 192 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 193 &states.ResourceInstanceObjectSrc{ 194 AttrsJSON: []byte(`{"id":"i-ab123"}`), 195 Status: states.ObjectReady, 196 }, 197 addrs.ProviderConfig{Type: addrs.NewLegacyProvider("test")}.Absolute(addrs.RootModuleInstance), 198 ) 199 s.SetResourceInstanceCurrent( 200 addrs.Resource{ 201 Mode: addrs.ManagedResourceMode, 202 Type: "test_load_balancer", 203 Name: "foo", 204 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 205 &states.ResourceInstanceObjectSrc{ 206 AttrsJSON: []byte(`{"id":"i-abc123"}`), 207 Status: states.ObjectReady, 208 }, 209 addrs.ProviderConfig{Type: addrs.NewLegacyProvider("test")}.Absolute(addrs.RootModuleInstance), 210 ) 211 }) 212 statePath := testStateFile(t, originalState) 213 214 p := testProvider() 215 p.GetSchemaReturn = &terraform.ProviderSchema{ 216 ResourceTypes: map[string]*configschema.Block{ 217 "test_instance": { 218 Attributes: map[string]*configschema.Attribute{ 219 "id": {Type: cty.String, Computed: true}, 220 }, 221 }, 222 "test_load_balancer": { 223 Attributes: map[string]*configschema.Attribute{ 224 "id": {Type: cty.String, Computed: true}, 225 "instances": {Type: cty.List(cty.String), Optional: true}, 226 }, 227 }, 228 }, 229 } 230 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { 231 return providers.PlanResourceChangeResponse{ 232 PlannedState: req.ProposedNewState, 233 } 234 } 235 236 ui := new(cli.MockUi) 237 c := &ApplyCommand{ 238 Destroy: true, 239 Meta: Meta{ 240 testingOverrides: metaOverridesForProvider(p), 241 Ui: ui, 242 }, 243 } 244 245 // Run the apply command pointing to our existing state 246 args := []string{ 247 "-auto-approve", 248 "-target", "test_instance.foo", 249 "-state", statePath, 250 testFixturePath("apply-destroy-targeted"), 251 } 252 if code := c.Run(args); code != 0 { 253 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 254 } 255 256 // Verify a new state exists 257 if _, err := os.Stat(statePath); err != nil { 258 t.Fatalf("err: %s", err) 259 } 260 261 f, err := os.Open(statePath) 262 if err != nil { 263 t.Fatalf("err: %s", err) 264 } 265 defer f.Close() 266 267 stateFile, err := statefile.Read(f) 268 if err != nil { 269 t.Fatalf("err: %s", err) 270 } 271 if stateFile == nil || stateFile.State == nil { 272 t.Fatal("state should not be nil") 273 } 274 275 spew.Config.DisableMethods = true 276 if !stateFile.State.Empty() { 277 t.Fatalf("unexpected final state\ngot: %s\nwant: empty state", spew.Sdump(stateFile.State)) 278 } 279 280 // Should have a backup file 281 f, err = os.Open(statePath + DefaultBackupExtension) 282 if err != nil { 283 t.Fatalf("err: %s", err) 284 } 285 286 backupStateFile, err := statefile.Read(f) 287 f.Close() 288 if err != nil { 289 t.Fatalf("err: %s", err) 290 } 291 292 actualStr := strings.TrimSpace(backupStateFile.State.String()) 293 expectedStr := strings.TrimSpace(originalState.String()) 294 if actualStr != expectedStr { 295 t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr) 296 } 297 } 298 299 const testApplyDestroyStr = ` 300 <no state> 301 `