github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/state_replace_provider_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "path/filepath" 6 "strings" 7 "testing" 8 9 "github.com/mitchellh/cli" 10 11 "github.com/hashicorp/terraform/internal/addrs" 12 "github.com/hashicorp/terraform/internal/states" 13 ) 14 15 func TestStateReplaceProvider(t *testing.T) { 16 state := states.BuildState(func(s *states.SyncState) { 17 s.SetResourceInstanceCurrent( 18 addrs.Resource{ 19 Mode: addrs.ManagedResourceMode, 20 Type: "aws_instance", 21 Name: "alpha", 22 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 23 &states.ResourceInstanceObjectSrc{ 24 AttrsJSON: []byte(`{"id":"alpha","foo":"value","bar":"value"}`), 25 Status: states.ObjectReady, 26 }, 27 addrs.AbsProviderConfig{ 28 Provider: addrs.NewDefaultProvider("aws"), 29 Module: addrs.RootModule, 30 }, 31 ) 32 s.SetResourceInstanceCurrent( 33 addrs.Resource{ 34 Mode: addrs.ManagedResourceMode, 35 Type: "aws_instance", 36 Name: "beta", 37 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 38 &states.ResourceInstanceObjectSrc{ 39 AttrsJSON: []byte(`{"id":"beta","foo":"value","bar":"value"}`), 40 Status: states.ObjectReady, 41 }, 42 addrs.AbsProviderConfig{ 43 Provider: addrs.NewDefaultProvider("aws"), 44 Module: addrs.RootModule, 45 }, 46 ) 47 s.SetResourceInstanceCurrent( 48 addrs.Resource{ 49 Mode: addrs.ManagedResourceMode, 50 Type: "azurerm_virtual_machine", 51 Name: "gamma", 52 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 53 &states.ResourceInstanceObjectSrc{ 54 AttrsJSON: []byte(`{"id":"gamma","baz":"value"}`), 55 Status: states.ObjectReady, 56 }, 57 addrs.AbsProviderConfig{ 58 Provider: addrs.NewLegacyProvider("azurerm"), 59 Module: addrs.RootModule, 60 }, 61 ) 62 }) 63 64 t.Run("happy path", func(t *testing.T) { 65 statePath := testStateFile(t, state) 66 67 ui := new(cli.MockUi) 68 view, _ := testView(t) 69 c := &StateReplaceProviderCommand{ 70 StateMeta{ 71 Meta: Meta{ 72 Ui: ui, 73 View: view, 74 }, 75 }, 76 } 77 78 inputBuf := &bytes.Buffer{} 79 ui.InputReader = inputBuf 80 inputBuf.WriteString("yes\n") 81 82 args := []string{ 83 "-state", statePath, 84 "hashicorp/aws", 85 "acmecorp/aws", 86 } 87 if code := c.Run(args); code != 0 { 88 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 89 } 90 91 testStateOutput(t, statePath, testStateReplaceProviderOutput) 92 93 backups := testStateBackups(t, filepath.Dir(statePath)) 94 if len(backups) != 1 { 95 t.Fatalf("unexpected backups: %#v", backups) 96 } 97 testStateOutput(t, backups[0], testStateReplaceProviderOutputOriginal) 98 }) 99 100 t.Run("auto approve", func(t *testing.T) { 101 statePath := testStateFile(t, state) 102 103 ui := new(cli.MockUi) 104 view, _ := testView(t) 105 c := &StateReplaceProviderCommand{ 106 StateMeta{ 107 Meta: Meta{ 108 Ui: ui, 109 View: view, 110 }, 111 }, 112 } 113 114 inputBuf := &bytes.Buffer{} 115 ui.InputReader = inputBuf 116 117 args := []string{ 118 "-state", statePath, 119 "-auto-approve", 120 "hashicorp/aws", 121 "acmecorp/aws", 122 } 123 if code := c.Run(args); code != 0 { 124 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 125 } 126 127 testStateOutput(t, statePath, testStateReplaceProviderOutput) 128 129 backups := testStateBackups(t, filepath.Dir(statePath)) 130 if len(backups) != 1 { 131 t.Fatalf("unexpected backups: %#v", backups) 132 } 133 testStateOutput(t, backups[0], testStateReplaceProviderOutputOriginal) 134 }) 135 136 t.Run("cancel at approval step", func(t *testing.T) { 137 statePath := testStateFile(t, state) 138 139 ui := new(cli.MockUi) 140 view, _ := testView(t) 141 c := &StateReplaceProviderCommand{ 142 StateMeta{ 143 Meta: Meta{ 144 Ui: ui, 145 View: view, 146 }, 147 }, 148 } 149 150 inputBuf := &bytes.Buffer{} 151 ui.InputReader = inputBuf 152 inputBuf.WriteString("no\n") 153 154 args := []string{ 155 "-state", statePath, 156 "hashicorp/aws", 157 "acmecorp/aws", 158 } 159 if code := c.Run(args); code != 0 { 160 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 161 } 162 163 testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal) 164 165 backups := testStateBackups(t, filepath.Dir(statePath)) 166 if len(backups) != 0 { 167 t.Fatalf("unexpected backups: %#v", backups) 168 } 169 }) 170 171 t.Run("no matching provider found", func(t *testing.T) { 172 statePath := testStateFile(t, state) 173 174 ui := new(cli.MockUi) 175 view, _ := testView(t) 176 c := &StateReplaceProviderCommand{ 177 StateMeta{ 178 Meta: Meta{ 179 Ui: ui, 180 View: view, 181 }, 182 }, 183 } 184 185 args := []string{ 186 "-state", statePath, 187 "hashicorp/google", 188 "acmecorp/google", 189 } 190 if code := c.Run(args); code != 0 { 191 t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String()) 192 } 193 194 testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal) 195 196 backups := testStateBackups(t, filepath.Dir(statePath)) 197 if len(backups) != 0 { 198 t.Fatalf("unexpected backups: %#v", backups) 199 } 200 }) 201 202 t.Run("invalid flags", func(t *testing.T) { 203 ui := new(cli.MockUi) 204 view, _ := testView(t) 205 c := &StateReplaceProviderCommand{ 206 StateMeta{ 207 Meta: Meta{ 208 Ui: ui, 209 View: view, 210 }, 211 }, 212 } 213 214 args := []string{ 215 "-invalid", 216 "hashicorp/google", 217 "acmecorp/google", 218 } 219 if code := c.Run(args); code == 0 { 220 t.Fatalf("successful exit; want error") 221 } 222 223 if got, want := ui.ErrorWriter.String(), "Error parsing command-line flags"; !strings.Contains(got, want) { 224 t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got) 225 } 226 }) 227 228 t.Run("wrong number of arguments", func(t *testing.T) { 229 ui := new(cli.MockUi) 230 view, _ := testView(t) 231 c := &StateReplaceProviderCommand{ 232 StateMeta{ 233 Meta: Meta{ 234 Ui: ui, 235 View: view, 236 }, 237 }, 238 } 239 240 args := []string{"a", "b", "c", "d"} 241 if code := c.Run(args); code == 0 { 242 t.Fatalf("successful exit; want error") 243 } 244 245 if got, want := ui.ErrorWriter.String(), "Exactly two arguments expected"; !strings.Contains(got, want) { 246 t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got) 247 } 248 }) 249 250 t.Run("invalid provider strings", func(t *testing.T) { 251 ui := new(cli.MockUi) 252 view, _ := testView(t) 253 c := &StateReplaceProviderCommand{ 254 StateMeta{ 255 Meta: Meta{ 256 Ui: ui, 257 View: view, 258 }, 259 }, 260 } 261 262 args := []string{ 263 "hashicorp/google_cloud", 264 "-/-/google", 265 } 266 if code := c.Run(args); code == 0 { 267 t.Fatalf("successful exit; want error") 268 } 269 270 got := ui.ErrorWriter.String() 271 msgs := []string{ 272 `Invalid "from" provider "hashicorp/google_cloud"`, 273 "Invalid provider type", 274 `Invalid "to" provider "-/-/google"`, 275 "Invalid provider source hostname", 276 } 277 for _, msg := range msgs { 278 if !strings.Contains(got, msg) { 279 t.Errorf("missing expected error message\nwant: %s\nfull output:\n%s", msg, got) 280 } 281 } 282 }) 283 } 284 285 func TestStateReplaceProvider_docs(t *testing.T) { 286 c := &StateReplaceProviderCommand{} 287 288 if got, want := c.Help(), "Usage: terraform [global options] state replace-provider"; !strings.Contains(got, want) { 289 t.Fatalf("unexpected help text\nwant: %s\nfull output:\n%s", want, got) 290 } 291 292 if got, want := c.Synopsis(), "Replace provider in the state"; got != want { 293 t.Fatalf("unexpected synopsis\nwant: %s\nfull output:\n%s", want, got) 294 } 295 } 296 297 func TestStateReplaceProvider_checkRequiredVersion(t *testing.T) { 298 // Create a temporary working directory that is empty 299 td := t.TempDir() 300 testCopyDir(t, testFixturePath("command-check-required-version"), td) 301 defer testChdir(t, td)() 302 303 state := states.BuildState(func(s *states.SyncState) { 304 s.SetResourceInstanceCurrent( 305 addrs.Resource{ 306 Mode: addrs.ManagedResourceMode, 307 Type: "aws_instance", 308 Name: "alpha", 309 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 310 &states.ResourceInstanceObjectSrc{ 311 AttrsJSON: []byte(`{"id":"alpha","foo":"value","bar":"value"}`), 312 Status: states.ObjectReady, 313 }, 314 addrs.AbsProviderConfig{ 315 Provider: addrs.NewDefaultProvider("aws"), 316 Module: addrs.RootModule, 317 }, 318 ) 319 s.SetResourceInstanceCurrent( 320 addrs.Resource{ 321 Mode: addrs.ManagedResourceMode, 322 Type: "aws_instance", 323 Name: "beta", 324 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 325 &states.ResourceInstanceObjectSrc{ 326 AttrsJSON: []byte(`{"id":"beta","foo":"value","bar":"value"}`), 327 Status: states.ObjectReady, 328 }, 329 addrs.AbsProviderConfig{ 330 Provider: addrs.NewDefaultProvider("aws"), 331 Module: addrs.RootModule, 332 }, 333 ) 334 s.SetResourceInstanceCurrent( 335 addrs.Resource{ 336 Mode: addrs.ManagedResourceMode, 337 Type: "azurerm_virtual_machine", 338 Name: "gamma", 339 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 340 &states.ResourceInstanceObjectSrc{ 341 AttrsJSON: []byte(`{"id":"gamma","baz":"value"}`), 342 Status: states.ObjectReady, 343 }, 344 addrs.AbsProviderConfig{ 345 Provider: addrs.NewLegacyProvider("azurerm"), 346 Module: addrs.RootModule, 347 }, 348 ) 349 }) 350 351 statePath := testStateFile(t, state) 352 353 ui := new(cli.MockUi) 354 view, _ := testView(t) 355 c := &StateReplaceProviderCommand{ 356 StateMeta{ 357 Meta: Meta{ 358 Ui: ui, 359 View: view, 360 }, 361 }, 362 } 363 364 inputBuf := &bytes.Buffer{} 365 ui.InputReader = inputBuf 366 inputBuf.WriteString("yes\n") 367 368 args := []string{ 369 "-state", statePath, 370 "hashicorp/aws", 371 "acmecorp/aws", 372 } 373 if code := c.Run(args); code != 1 { 374 t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String()) 375 } 376 377 // State is unchanged 378 testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal) 379 380 // Required version diags are correct 381 errStr := ui.ErrorWriter.String() 382 if !strings.Contains(errStr, `required_version = "~> 0.9.0"`) { 383 t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr) 384 } 385 if strings.Contains(errStr, `required_version = ">= 0.13.0"`) { 386 t.Fatalf("output should not point to met version constraint, but is:\n\n%s", errStr) 387 } 388 } 389 390 const testStateReplaceProviderOutputOriginal = ` 391 aws_instance.alpha: 392 ID = alpha 393 provider = provider["registry.terraform.io/hashicorp/aws"] 394 bar = value 395 foo = value 396 aws_instance.beta: 397 ID = beta 398 provider = provider["registry.terraform.io/hashicorp/aws"] 399 bar = value 400 foo = value 401 azurerm_virtual_machine.gamma: 402 ID = gamma 403 provider = provider["registry.terraform.io/-/azurerm"] 404 baz = value 405 ` 406 407 const testStateReplaceProviderOutput = ` 408 aws_instance.alpha: 409 ID = alpha 410 provider = provider["registry.terraform.io/acmecorp/aws"] 411 bar = value 412 foo = value 413 aws_instance.beta: 414 ID = beta 415 provider = provider["registry.terraform.io/acmecorp/aws"] 416 bar = value 417 foo = value 418 azurerm_virtual_machine.gamma: 419 ID = gamma 420 provider = provider["registry.terraform.io/-/azurerm"] 421 baz = value 422 `