github.com/trawler/terraform@v0.10.8-0.20171106022149-4b1c7a1d9b48/terraform/context_input_test.go (about) 1 package terraform 2 3 import ( 4 "errors" 5 "reflect" 6 "strings" 7 "sync" 8 "testing" 9 ) 10 11 func TestContext2Input(t *testing.T) { 12 input := new(MockUIInput) 13 m := testModule(t, "input-vars") 14 p := testProvider("aws") 15 p.ApplyFn = testApplyFn 16 p.DiffFn = testDiffFn 17 ctx := testContext2(t, &ContextOpts{ 18 Module: m, 19 ProviderResolver: ResourceProviderResolverFixed( 20 map[string]ResourceProviderFactory{ 21 "aws": testProviderFuncFixed(p), 22 }, 23 ), 24 Variables: map[string]interface{}{ 25 "foo": "us-west-2", 26 "amis": []map[string]interface{}{ 27 map[string]interface{}{ 28 "us-east-1": "override", 29 }, 30 }, 31 }, 32 UIInput: input, 33 }) 34 35 input.InputReturnMap = map[string]string{ 36 "var.foo": "us-east-1", 37 } 38 39 if err := ctx.Input(InputModeStd); err != nil { 40 t.Fatalf("err: %s", err) 41 } 42 43 if _, err := ctx.Plan(); err != nil { 44 t.Fatalf("err: %s", err) 45 } 46 47 state, err := ctx.Apply() 48 if err != nil { 49 t.Fatalf("err: %s", err) 50 } 51 52 actual := strings.TrimSpace(state.String()) 53 expected := strings.TrimSpace(testTerraformInputVarsStr) 54 if actual != expected { 55 t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual) 56 } 57 } 58 59 func TestContext2Input_moduleComputedOutputElement(t *testing.T) { 60 m := testModule(t, "input-module-computed-output-element") 61 p := testProvider("aws") 62 p.ApplyFn = testApplyFn 63 p.DiffFn = testDiffFn 64 ctx := testContext2(t, &ContextOpts{ 65 Module: m, 66 ProviderResolver: ResourceProviderResolverFixed( 67 map[string]ResourceProviderFactory{ 68 "aws": testProviderFuncFixed(p), 69 }, 70 ), 71 }) 72 73 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 74 return c, nil 75 } 76 77 if err := ctx.Input(InputModeStd); err != nil { 78 t.Fatalf("err: %s", err) 79 } 80 } 81 82 func TestContext2Input_badVarDefault(t *testing.T) { 83 m := testModule(t, "input-bad-var-default") 84 p := testProvider("aws") 85 p.ApplyFn = testApplyFn 86 p.DiffFn = testDiffFn 87 ctx := testContext2(t, &ContextOpts{ 88 Module: m, 89 ProviderResolver: ResourceProviderResolverFixed( 90 map[string]ResourceProviderFactory{ 91 "aws": testProviderFuncFixed(p), 92 }, 93 ), 94 }) 95 96 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 97 c.Config["foo"] = "bar" 98 return c, nil 99 } 100 101 if err := ctx.Input(InputModeStd); err != nil { 102 t.Fatalf("err: %s", err) 103 } 104 } 105 106 func TestContext2Input_provider(t *testing.T) { 107 m := testModule(t, "input-provider") 108 p := testProvider("aws") 109 p.ApplyFn = testApplyFn 110 p.DiffFn = testDiffFn 111 ctx := testContext2(t, &ContextOpts{ 112 Module: m, 113 ProviderResolver: ResourceProviderResolverFixed( 114 map[string]ResourceProviderFactory{ 115 "aws": testProviderFuncFixed(p), 116 }, 117 ), 118 }) 119 120 var actual interface{} 121 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 122 c.Config["foo"] = "bar" 123 return c, nil 124 } 125 p.ConfigureFn = func(c *ResourceConfig) error { 126 actual = c.Config["foo"] 127 return nil 128 } 129 p.ValidateFn = func(c *ResourceConfig) ([]string, []error) { 130 return nil, c.CheckSet([]string{"foo"}) 131 } 132 133 if err := ctx.Input(InputModeStd); err != nil { 134 t.Fatalf("err: %s", err) 135 } 136 137 if _, err := ctx.Plan(); err != nil { 138 t.Fatalf("err: %s", err) 139 } 140 141 if _, err := ctx.Apply(); err != nil { 142 t.Fatalf("err: %s", err) 143 } 144 145 if !reflect.DeepEqual(actual, "bar") { 146 t.Fatalf("bad: %#v", actual) 147 } 148 } 149 150 func TestContext2Input_providerMulti(t *testing.T) { 151 m := testModule(t, "input-provider-multi") 152 p := testProvider("aws") 153 p.ApplyFn = testApplyFn 154 p.DiffFn = testDiffFn 155 ctx := testContext2(t, &ContextOpts{ 156 Module: m, 157 ProviderResolver: ResourceProviderResolverFixed( 158 map[string]ResourceProviderFactory{ 159 "aws": testProviderFuncFixed(p), 160 }, 161 ), 162 }) 163 164 var actual []interface{} 165 var lock sync.Mutex 166 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 167 c.Config["foo"] = "bar" 168 return c, nil 169 } 170 p.ConfigureFn = func(c *ResourceConfig) error { 171 lock.Lock() 172 defer lock.Unlock() 173 actual = append(actual, c.Config["foo"]) 174 return nil 175 } 176 p.ValidateFn = func(c *ResourceConfig) ([]string, []error) { 177 return nil, c.CheckSet([]string{"foo"}) 178 } 179 180 if err := ctx.Input(InputModeStd); err != nil { 181 t.Fatalf("err: %s", err) 182 } 183 184 if _, err := ctx.Plan(); err != nil { 185 t.Fatalf("err: %s", err) 186 } 187 188 if _, err := ctx.Apply(); err != nil { 189 t.Fatalf("err: %s", err) 190 } 191 192 expected := []interface{}{"bar", "bar"} 193 if !reflect.DeepEqual(actual, expected) { 194 t.Fatalf("bad: %#v", actual) 195 } 196 } 197 198 func TestContext2Input_providerOnce(t *testing.T) { 199 m := testModule(t, "input-provider-once") 200 p := testProvider("aws") 201 p.ApplyFn = testApplyFn 202 p.DiffFn = testDiffFn 203 ctx := testContext2(t, &ContextOpts{ 204 Module: m, 205 ProviderResolver: ResourceProviderResolverFixed( 206 map[string]ResourceProviderFactory{ 207 "aws": testProviderFuncFixed(p), 208 }, 209 ), 210 }) 211 212 count := 0 213 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 214 count++ 215 _, set := c.Config["from_input"] 216 217 if count == 1 { 218 if set { 219 return nil, errors.New("from_input should not be set") 220 } 221 c.Config["from_input"] = "x" 222 } 223 224 if count > 1 && !set { 225 return nil, errors.New("from_input should be set") 226 } 227 228 return c, nil 229 } 230 231 if err := ctx.Input(InputModeStd); err != nil { 232 t.Fatalf("err: %s", err) 233 } 234 } 235 236 func TestContext2Input_providerId(t *testing.T) { 237 input := new(MockUIInput) 238 m := testModule(t, "input-provider") 239 p := testProvider("aws") 240 p.ApplyFn = testApplyFn 241 p.DiffFn = testDiffFn 242 ctx := testContext2(t, &ContextOpts{ 243 Module: m, 244 ProviderResolver: ResourceProviderResolverFixed( 245 map[string]ResourceProviderFactory{ 246 "aws": testProviderFuncFixed(p), 247 }, 248 ), 249 UIInput: input, 250 }) 251 252 var actual interface{} 253 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 254 v, err := i.Input(&InputOpts{Id: "foo"}) 255 if err != nil { 256 return nil, err 257 } 258 259 c.Config["foo"] = v 260 return c, nil 261 } 262 p.ConfigureFn = func(c *ResourceConfig) error { 263 actual = c.Config["foo"] 264 return nil 265 } 266 267 input.InputReturnMap = map[string]string{ 268 "provider.aws.foo": "bar", 269 } 270 271 if err := ctx.Input(InputModeStd); err != nil { 272 t.Fatalf("err: %s", err) 273 } 274 275 if _, err := ctx.Plan(); err != nil { 276 t.Fatalf("err: %s", err) 277 } 278 279 if _, err := ctx.Apply(); err != nil { 280 t.Fatalf("err: %s", err) 281 } 282 283 if !reflect.DeepEqual(actual, "bar") { 284 t.Fatalf("bad: %#v", actual) 285 } 286 } 287 288 func TestContext2Input_providerOnly(t *testing.T) { 289 input := new(MockUIInput) 290 m := testModule(t, "input-provider-vars") 291 p := testProvider("aws") 292 p.ApplyFn = testApplyFn 293 p.DiffFn = testDiffFn 294 ctx := testContext2(t, &ContextOpts{ 295 Module: m, 296 ProviderResolver: ResourceProviderResolverFixed( 297 map[string]ResourceProviderFactory{ 298 "aws": testProviderFuncFixed(p), 299 }, 300 ), 301 Variables: map[string]interface{}{ 302 "foo": "us-west-2", 303 }, 304 UIInput: input, 305 }) 306 307 input.InputReturnMap = map[string]string{ 308 "var.foo": "us-east-1", 309 } 310 311 var actual interface{} 312 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 313 c.Config["foo"] = "bar" 314 return c, nil 315 } 316 p.ConfigureFn = func(c *ResourceConfig) error { 317 actual = c.Config["foo"] 318 return nil 319 } 320 321 if err := ctx.Input(InputModeProvider); err != nil { 322 t.Fatalf("err: %s", err) 323 } 324 325 if _, err := ctx.Plan(); err != nil { 326 t.Fatalf("err: %s", err) 327 } 328 329 state, err := ctx.Apply() 330 if err != nil { 331 t.Fatalf("err: %s", err) 332 } 333 334 if !reflect.DeepEqual(actual, "bar") { 335 t.Fatalf("bad: %#v", actual) 336 } 337 338 actualStr := strings.TrimSpace(state.String()) 339 expectedStr := strings.TrimSpace(testTerraformInputProviderOnlyStr) 340 if actualStr != expectedStr { 341 t.Fatalf("bad: \n%s", actualStr) 342 } 343 } 344 345 func TestContext2Input_providerVars(t *testing.T) { 346 input := new(MockUIInput) 347 m := testModule(t, "input-provider-with-vars") 348 p := testProvider("aws") 349 p.ApplyFn = testApplyFn 350 p.DiffFn = testDiffFn 351 ctx := testContext2(t, &ContextOpts{ 352 Module: m, 353 ProviderResolver: ResourceProviderResolverFixed( 354 map[string]ResourceProviderFactory{ 355 "aws": testProviderFuncFixed(p), 356 }, 357 ), 358 Variables: map[string]interface{}{ 359 "foo": "bar", 360 }, 361 UIInput: input, 362 }) 363 364 input.InputReturnMap = map[string]string{ 365 "var.foo": "bar", 366 } 367 368 var actual interface{} 369 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 370 c.Config["bar"] = "baz" 371 return c, nil 372 } 373 p.ConfigureFn = func(c *ResourceConfig) error { 374 actual, _ = c.Get("foo") 375 return nil 376 } 377 378 if err := ctx.Input(InputModeStd); err != nil { 379 t.Fatalf("err: %s", err) 380 } 381 382 if _, err := ctx.Plan(); err != nil { 383 t.Fatalf("err: %s", err) 384 } 385 386 if _, err := ctx.Apply(); err != nil { 387 t.Fatalf("err: %s", err) 388 } 389 390 if !reflect.DeepEqual(actual, "bar") { 391 t.Fatalf("bad: %#v", actual) 392 } 393 } 394 395 func TestContext2Input_providerVarsModuleInherit(t *testing.T) { 396 input := new(MockUIInput) 397 m := testModule(t, "input-provider-with-vars-and-module") 398 p := testProvider("aws") 399 p.ApplyFn = testApplyFn 400 p.DiffFn = testDiffFn 401 ctx := testContext2(t, &ContextOpts{ 402 Module: m, 403 ProviderResolver: ResourceProviderResolverFixed( 404 map[string]ResourceProviderFactory{ 405 "aws": testProviderFuncFixed(p), 406 }, 407 ), 408 UIInput: input, 409 }) 410 411 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 412 if errs := c.CheckSet([]string{"access_key"}); len(errs) > 0 { 413 return c, errs[0] 414 } 415 return c, nil 416 } 417 p.ConfigureFn = func(c *ResourceConfig) error { 418 return nil 419 } 420 421 if err := ctx.Input(InputModeStd); err != nil { 422 t.Fatalf("err: %s", err) 423 } 424 } 425 426 func TestContext2Input_varOnly(t *testing.T) { 427 input := new(MockUIInput) 428 m := testModule(t, "input-provider-vars") 429 p := testProvider("aws") 430 p.ApplyFn = testApplyFn 431 p.DiffFn = testDiffFn 432 ctx := testContext2(t, &ContextOpts{ 433 Module: m, 434 ProviderResolver: ResourceProviderResolverFixed( 435 map[string]ResourceProviderFactory{ 436 "aws": testProviderFuncFixed(p), 437 }, 438 ), 439 Variables: map[string]interface{}{ 440 "foo": "us-west-2", 441 }, 442 UIInput: input, 443 }) 444 445 input.InputReturnMap = map[string]string{ 446 "var.foo": "us-east-1", 447 } 448 449 var actual interface{} 450 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 451 c.Raw["foo"] = "bar" 452 return c, nil 453 } 454 p.ConfigureFn = func(c *ResourceConfig) error { 455 actual = c.Raw["foo"] 456 return nil 457 } 458 459 if err := ctx.Input(InputModeVar); err != nil { 460 t.Fatalf("err: %s", err) 461 } 462 463 if _, err := ctx.Plan(); err != nil { 464 t.Fatalf("err: %s", err) 465 } 466 467 state, err := ctx.Apply() 468 if err != nil { 469 t.Fatalf("err: %s", err) 470 } 471 472 if reflect.DeepEqual(actual, "bar") { 473 t.Fatalf("bad: %#v", actual) 474 } 475 476 actualStr := strings.TrimSpace(state.String()) 477 expectedStr := strings.TrimSpace(testTerraformInputVarOnlyStr) 478 if actualStr != expectedStr { 479 t.Fatalf("bad: \n%s", actualStr) 480 } 481 } 482 483 func TestContext2Input_varOnlyUnset(t *testing.T) { 484 input := new(MockUIInput) 485 m := testModule(t, "input-vars-unset") 486 p := testProvider("aws") 487 p.ApplyFn = testApplyFn 488 p.DiffFn = testDiffFn 489 ctx := testContext2(t, &ContextOpts{ 490 Module: m, 491 ProviderResolver: ResourceProviderResolverFixed( 492 map[string]ResourceProviderFactory{ 493 "aws": testProviderFuncFixed(p), 494 }, 495 ), 496 Variables: map[string]interface{}{ 497 "foo": "foovalue", 498 }, 499 UIInput: input, 500 }) 501 502 input.InputReturnMap = map[string]string{ 503 "var.foo": "nope", 504 "var.bar": "baz", 505 } 506 507 if err := ctx.Input(InputModeVar | InputModeVarUnset); err != nil { 508 t.Fatalf("err: %s", err) 509 } 510 511 if _, err := ctx.Plan(); err != nil { 512 t.Fatalf("err: %s", err) 513 } 514 515 state, err := ctx.Apply() 516 if err != nil { 517 t.Fatalf("err: %s", err) 518 } 519 520 actualStr := strings.TrimSpace(state.String()) 521 expectedStr := strings.TrimSpace(testTerraformInputVarOnlyUnsetStr) 522 if actualStr != expectedStr { 523 t.Fatalf("bad: \n%s", actualStr) 524 } 525 } 526 527 func TestContext2Input_varWithDefault(t *testing.T) { 528 input := new(MockUIInput) 529 m := testModule(t, "input-var-default") 530 p := testProvider("aws") 531 p.ApplyFn = testApplyFn 532 p.DiffFn = testDiffFn 533 ctx := testContext2(t, &ContextOpts{ 534 Module: m, 535 ProviderResolver: ResourceProviderResolverFixed( 536 map[string]ResourceProviderFactory{ 537 "aws": testProviderFuncFixed(p), 538 }, 539 ), 540 Variables: map[string]interface{}{}, 541 UIInput: input, 542 }) 543 544 input.InputFn = func(opts *InputOpts) (string, error) { 545 t.Fatalf( 546 "Input should never be called because variable has a default: %#v", opts) 547 return "", nil 548 } 549 550 if err := ctx.Input(InputModeVar | InputModeVarUnset); err != nil { 551 t.Fatalf("err: %s", err) 552 } 553 554 if _, err := ctx.Plan(); err != nil { 555 t.Fatalf("err: %s", err) 556 } 557 558 state, err := ctx.Apply() 559 if err != nil { 560 t.Fatalf("err: %s", err) 561 } 562 563 actualStr := strings.TrimSpace(state.String()) 564 expectedStr := strings.TrimSpace(` 565 aws_instance.foo: 566 ID = foo 567 foo = 123 568 type = aws_instance 569 `) 570 if actualStr != expectedStr { 571 t.Fatalf("expected: \n%s\ngot: \n%s\n", expectedStr, actualStr) 572 } 573 } 574 575 func TestContext2Input_varPartiallyComputed(t *testing.T) { 576 input := new(MockUIInput) 577 m := testModule(t, "input-var-partially-computed") 578 p := testProvider("aws") 579 p.ApplyFn = testApplyFn 580 p.DiffFn = testDiffFn 581 ctx := testContext2(t, &ContextOpts{ 582 Module: m, 583 ProviderResolver: ResourceProviderResolverFixed( 584 map[string]ResourceProviderFactory{ 585 "aws": testProviderFuncFixed(p), 586 }, 587 ), 588 Variables: map[string]interface{}{ 589 "foo": "foovalue", 590 }, 591 UIInput: input, 592 State: &State{ 593 Modules: []*ModuleState{ 594 &ModuleState{ 595 Path: rootModulePath, 596 Resources: map[string]*ResourceState{ 597 "aws_instance.foo": &ResourceState{ 598 Type: "aws_instance", 599 Primary: &InstanceState{ 600 ID: "i-abc123", 601 Attributes: map[string]string{ 602 "id": "i-abc123", 603 }, 604 }, 605 }, 606 }, 607 }, 608 &ModuleState{ 609 Path: append(rootModulePath, "child"), 610 Resources: map[string]*ResourceState{ 611 "aws_instance.mod": &ResourceState{ 612 Type: "aws_instance", 613 Primary: &InstanceState{ 614 ID: "i-bcd345", 615 Attributes: map[string]string{ 616 "id": "i-bcd345", 617 "value": "one,i-abc123", 618 }, 619 }, 620 }, 621 }, 622 }, 623 }, 624 }, 625 }) 626 627 if err := ctx.Input(InputModeStd); err != nil { 628 t.Fatalf("err: %s", err) 629 } 630 631 if _, err := ctx.Plan(); err != nil { 632 t.Fatalf("err: %s", err) 633 } 634 } 635 636 // Module variables weren't being interpolated during the Input walk. 637 // https://github.com/hashicorp/terraform/issues/5322 638 func TestContext2Input_interpolateVar(t *testing.T) { 639 input := new(MockUIInput) 640 641 m := testModule(t, "input-interpolate-var") 642 p := testProvider("null") 643 p.ApplyFn = testApplyFn 644 p.DiffFn = testDiffFn 645 646 ctx := testContext2(t, &ContextOpts{ 647 Module: m, 648 ProviderResolver: ResourceProviderResolverFixed( 649 map[string]ResourceProviderFactory{ 650 "template": testProviderFuncFixed(p), 651 }, 652 ), 653 UIInput: input, 654 }) 655 656 if err := ctx.Input(InputModeStd); err != nil { 657 t.Fatalf("err: %s", err) 658 } 659 } 660 661 func TestContext2Input_hcl(t *testing.T) { 662 input := new(MockUIInput) 663 m := testModule(t, "input-hcl") 664 p := testProvider("hcl") 665 p.ApplyFn = testApplyFn 666 p.DiffFn = testDiffFn 667 ctx := testContext2(t, &ContextOpts{ 668 Module: m, 669 ProviderResolver: ResourceProviderResolverFixed( 670 map[string]ResourceProviderFactory{ 671 "hcl": testProviderFuncFixed(p), 672 }, 673 ), 674 Variables: map[string]interface{}{}, 675 UIInput: input, 676 }) 677 678 input.InputReturnMap = map[string]string{ 679 "var.listed": `["a", "b"]`, 680 "var.mapped": `{x = "y", w = "z"}`, 681 } 682 683 if err := ctx.Input(InputModeVar | InputModeVarUnset); err != nil { 684 t.Fatalf("err: %s", err) 685 } 686 687 if _, err := ctx.Plan(); err != nil { 688 t.Fatalf("err: %s", err) 689 } 690 691 state, err := ctx.Apply() 692 if err != nil { 693 t.Fatalf("err: %s", err) 694 } 695 696 actualStr := strings.TrimSpace(state.String()) 697 expectedStr := strings.TrimSpace(testTerraformInputHCL) 698 if actualStr != expectedStr { 699 t.Logf("expected: \n%s", expectedStr) 700 t.Fatalf("bad: \n%s", actualStr) 701 } 702 } 703 704 // adding a list interpolation in fails to interpolate the count variable 705 func TestContext2Input_submoduleTriggersInvalidCount(t *testing.T) { 706 input := new(MockUIInput) 707 m := testModule(t, "input-submodule-count") 708 p := testProvider("aws") 709 p.ApplyFn = testApplyFn 710 p.DiffFn = testDiffFn 711 ctx := testContext2(t, &ContextOpts{ 712 Module: m, 713 ProviderResolver: ResourceProviderResolverFixed( 714 map[string]ResourceProviderFactory{ 715 "aws": testProviderFuncFixed(p), 716 }, 717 ), 718 UIInput: input, 719 }) 720 721 p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { 722 return c, nil 723 } 724 p.ConfigureFn = func(c *ResourceConfig) error { 725 return nil 726 } 727 728 if err := ctx.Input(InputModeStd); err != nil { 729 t.Fatalf("err: %s", err) 730 } 731 } 732 733 // In this case, a module variable can't be resolved from a data source until 734 // it's refreshed, but it can't be refreshed during Input. 735 func TestContext2Input_dataSourceRequiresRefresh(t *testing.T) { 736 input := new(MockUIInput) 737 p := testProvider("null") 738 m := testModule(t, "input-module-data-vars") 739 740 p.ReadDataDiffFn = testDataDiffFn 741 742 state := &State{ 743 Modules: []*ModuleState{ 744 &ModuleState{ 745 Path: rootModulePath, 746 Resources: map[string]*ResourceState{ 747 "data.null_data_source.bar": &ResourceState{ 748 Type: "null_data_source", 749 Primary: &InstanceState{ 750 ID: "-", 751 Attributes: map[string]string{ 752 "foo.#": "1", 753 "foo.0": "a", 754 // foo.1 exists in the data source, but needs to be refreshed. 755 }, 756 }, 757 }, 758 }, 759 }, 760 }, 761 } 762 763 ctx := testContext2(t, &ContextOpts{ 764 Module: m, 765 ProviderResolver: ResourceProviderResolverFixed( 766 map[string]ResourceProviderFactory{ 767 "null": testProviderFuncFixed(p), 768 }, 769 ), 770 State: state, 771 UIInput: input, 772 }) 773 774 if err := ctx.Input(InputModeStd); err != nil { 775 t.Fatalf("err: %s", err) 776 } 777 778 // ensure that plan works after Refresh 779 if _, err := ctx.Refresh(); err != nil { 780 t.Fatalf("err: %s", err) 781 } 782 if _, err := ctx.Plan(); err != nil { 783 t.Fatalf("err: %s", err) 784 } 785 }