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