github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/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/muratcelep/terraform/not-internal/addrs" 12 "github.com/muratcelep/terraform/not-internal/configs/configschema" 13 "github.com/muratcelep/terraform/not-internal/plans" 14 "github.com/muratcelep/terraform/not-internal/providers" 15 "github.com/muratcelep/terraform/not-internal/states" 16 ) 17 18 func TestContext2Input_provider(t *testing.T) { 19 m := testModule(t, "input-provider") 20 p := testProvider("aws") 21 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 22 Provider: &configschema.Block{ 23 Attributes: map[string]*configschema.Attribute{ 24 "foo": { 25 Type: cty.String, 26 Required: true, 27 Description: "something something", 28 }, 29 }, 30 }, 31 ResourceTypes: map[string]*configschema.Block{ 32 "aws_instance": { 33 Attributes: map[string]*configschema.Attribute{ 34 "id": { 35 Type: cty.String, 36 Computed: true, 37 }, 38 }, 39 }, 40 }, 41 }) 42 43 inp := &MockUIInput{ 44 InputReturnMap: map[string]string{ 45 "provider.aws.foo": "bar", 46 }, 47 } 48 49 ctx := testContext2(t, &ContextOpts{ 50 Providers: map[addrs.Provider]providers.Factory{ 51 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 52 }, 53 UIInput: inp, 54 }) 55 56 var actual interface{} 57 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 58 actual = req.Config.GetAttr("foo").AsString() 59 return 60 } 61 62 if diags := ctx.Input(m, InputModeStd); diags.HasErrors() { 63 t.Fatalf("input errors: %s", diags.Err()) 64 } 65 66 if !inp.InputCalled { 67 t.Fatal("no input prompt; want prompt for argument \"foo\"") 68 } 69 if got, want := inp.InputOpts.Description, "something something"; got != want { 70 t.Errorf("wrong description\ngot: %q\nwant: %q", got, want) 71 } 72 73 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 74 assertNoErrors(t, diags) 75 76 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 77 t.Fatalf("apply errors: %s", diags.Err()) 78 } 79 80 if !reflect.DeepEqual(actual, "bar") { 81 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", actual, "bar") 82 } 83 } 84 85 func TestContext2Input_providerMulti(t *testing.T) { 86 m := testModule(t, "input-provider-multi") 87 88 getProviderSchemaResponse := getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 89 Provider: &configschema.Block{ 90 Attributes: map[string]*configschema.Attribute{ 91 "foo": { 92 Type: cty.String, 93 Required: true, 94 Description: "something something", 95 }, 96 }, 97 }, 98 ResourceTypes: map[string]*configschema.Block{ 99 "aws_instance": { 100 Attributes: map[string]*configschema.Attribute{ 101 "id": { 102 Type: cty.String, 103 Computed: true, 104 }, 105 }, 106 }, 107 }, 108 }) 109 110 // In order to update the provider to check only the configure calls during 111 // apply, we will need to inject a new factory function after plan. We must 112 // use a closure around the factory, because in order for the inputs to 113 // work during apply we need to maintain the same context value, preventing 114 // us from assigning a new Providers map. 115 providerFactory := func() (providers.Interface, error) { 116 p := testProvider("aws") 117 p.GetProviderSchemaResponse = getProviderSchemaResponse 118 return p, nil 119 } 120 121 inp := &MockUIInput{ 122 InputReturnMap: map[string]string{ 123 "provider.aws.foo": "bar", 124 "provider.aws.east.foo": "bar", 125 }, 126 } 127 128 ctx := testContext2(t, &ContextOpts{ 129 Providers: map[addrs.Provider]providers.Factory{ 130 addrs.NewDefaultProvider("aws"): func() (providers.Interface, error) { 131 return providerFactory() 132 }, 133 }, 134 UIInput: inp, 135 }) 136 137 var actual []interface{} 138 var lock sync.Mutex 139 140 if diags := ctx.Input(m, InputModeStd); diags.HasErrors() { 141 t.Fatalf("input errors: %s", diags.Err()) 142 } 143 144 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 145 assertNoErrors(t, diags) 146 147 providerFactory = func() (providers.Interface, error) { 148 p := testProvider("aws") 149 p.GetProviderSchemaResponse = getProviderSchemaResponse 150 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 151 lock.Lock() 152 defer lock.Unlock() 153 actual = append(actual, req.Config.GetAttr("foo").AsString()) 154 return 155 } 156 return p, nil 157 } 158 159 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 160 t.Fatalf("apply errors: %s", diags.Err()) 161 } 162 163 expected := []interface{}{"bar", "bar"} 164 if !reflect.DeepEqual(actual, expected) { 165 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", actual, expected) 166 } 167 } 168 169 func TestContext2Input_providerOnce(t *testing.T) { 170 m := testModule(t, "input-provider-once") 171 p := testProvider("aws") 172 ctx := testContext2(t, &ContextOpts{ 173 Providers: map[addrs.Provider]providers.Factory{ 174 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 175 }, 176 }) 177 178 if diags := ctx.Input(m, InputModeStd); diags.HasErrors() { 179 t.Fatalf("input errors: %s", diags.Err()) 180 } 181 } 182 183 func TestContext2Input_providerId(t *testing.T) { 184 input := new(MockUIInput) 185 186 m := testModule(t, "input-provider") 187 188 p := testProvider("aws") 189 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 190 Provider: &configschema.Block{ 191 Attributes: map[string]*configschema.Attribute{ 192 "foo": { 193 Type: cty.String, 194 Required: true, 195 Description: "something something", 196 }, 197 }, 198 }, 199 ResourceTypes: map[string]*configschema.Block{ 200 "aws_instance": { 201 Attributes: map[string]*configschema.Attribute{ 202 "id": { 203 Type: cty.String, 204 Computed: true, 205 }, 206 }, 207 }, 208 }, 209 }) 210 211 ctx := testContext2(t, &ContextOpts{ 212 Providers: map[addrs.Provider]providers.Factory{ 213 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 214 }, 215 UIInput: input, 216 }) 217 218 var actual interface{} 219 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 220 actual = req.Config.GetAttr("foo").AsString() 221 return 222 } 223 224 input.InputReturnMap = map[string]string{ 225 "provider.aws.foo": "bar", 226 } 227 228 if diags := ctx.Input(m, InputModeStd); diags.HasErrors() { 229 t.Fatalf("input errors: %s", diags.Err()) 230 } 231 232 plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 233 assertNoErrors(t, diags) 234 235 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 236 t.Fatalf("apply errors: %s", diags.Err()) 237 } 238 239 if !reflect.DeepEqual(actual, "bar") { 240 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", actual, "bar") 241 } 242 } 243 244 func TestContext2Input_providerOnly(t *testing.T) { 245 input := new(MockUIInput) 246 247 m := testModule(t, "input-provider-vars") 248 p := testProvider("aws") 249 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 250 Provider: &configschema.Block{ 251 Attributes: map[string]*configschema.Attribute{ 252 "foo": { 253 Type: cty.String, 254 Required: true, 255 }, 256 }, 257 }, 258 ResourceTypes: map[string]*configschema.Block{ 259 "aws_instance": { 260 Attributes: map[string]*configschema.Attribute{ 261 "foo": {Type: cty.String, Required: true}, 262 "id": {Type: cty.String, Computed: true}, 263 "type": {Type: cty.String, Computed: true}, 264 }, 265 }, 266 }, 267 }) 268 269 ctx := testContext2(t, &ContextOpts{ 270 Providers: map[addrs.Provider]providers.Factory{ 271 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 272 }, 273 UIInput: input, 274 }) 275 276 input.InputReturnMap = map[string]string{ 277 "provider.aws.foo": "bar", 278 } 279 280 var actual interface{} 281 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 282 actual = req.Config.GetAttr("foo").AsString() 283 return 284 } 285 286 if err := ctx.Input(m, InputModeProvider); err != nil { 287 t.Fatalf("err: %s", err) 288 } 289 290 // NOTE: This is a stale test case from an older version of Terraform 291 // where Input was responsible for prompting for both input variables _and_ 292 // provider configuration arguments, where it was trying to test the case 293 // where we were turning off the mode of prompting for input variables. 294 // That's now always disabled, and so this is essentially the same as the 295 // normal Input test, but we're preserving it until we have time to review 296 // and make sure this isn't inadvertently providing unique test coverage 297 // other than what it set out to test. 298 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 299 Mode: plans.NormalMode, 300 SetVariables: InputValues{ 301 "foo": &InputValue{ 302 Value: cty.StringVal("us-west-2"), 303 SourceType: ValueFromCaller, 304 }, 305 }, 306 }) 307 assertNoErrors(t, diags) 308 309 state, err := ctx.Apply(plan, m) 310 if err != nil { 311 t.Fatalf("err: %s", err) 312 } 313 314 if !reflect.DeepEqual(actual, "bar") { 315 t.Fatalf("wrong result\ngot: %#v\nwant: %#v", actual, "bar") 316 } 317 318 actualStr := strings.TrimSpace(state.String()) 319 expectedStr := strings.TrimSpace(testTerraformInputProviderOnlyStr) 320 if actualStr != expectedStr { 321 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actualStr, expectedStr) 322 } 323 } 324 325 func TestContext2Input_providerVars(t *testing.T) { 326 input := new(MockUIInput) 327 m := testModule(t, "input-provider-with-vars") 328 p := testProvider("aws") 329 ctx := testContext2(t, &ContextOpts{ 330 Providers: map[addrs.Provider]providers.Factory{ 331 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 332 }, 333 UIInput: input, 334 }) 335 336 input.InputReturnMap = map[string]string{ 337 "var.foo": "bar", 338 } 339 340 var actual interface{} 341 p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 342 actual = req.Config.GetAttr("foo").AsString() 343 return 344 } 345 if diags := ctx.Input(m, InputModeStd); diags.HasErrors() { 346 t.Fatalf("input errors: %s", diags.Err()) 347 } 348 349 plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ 350 Mode: plans.NormalMode, 351 SetVariables: InputValues{ 352 "foo": &InputValue{ 353 Value: cty.StringVal("bar"), 354 SourceType: ValueFromCaller, 355 }, 356 }, 357 }) 358 assertNoErrors(t, diags) 359 360 if _, diags := ctx.Apply(plan, m); diags.HasErrors() { 361 t.Fatalf("apply errors: %s", diags.Err()) 362 } 363 364 if !reflect.DeepEqual(actual, "bar") { 365 t.Fatalf("bad: %#v", actual) 366 } 367 } 368 369 func TestContext2Input_providerVarsModuleInherit(t *testing.T) { 370 input := new(MockUIInput) 371 m := testModule(t, "input-provider-with-vars-and-module") 372 p := testProvider("aws") 373 ctx := testContext2(t, &ContextOpts{ 374 Providers: map[addrs.Provider]providers.Factory{ 375 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 376 }, 377 UIInput: input, 378 }) 379 380 if diags := ctx.Input(m, InputModeStd); diags.HasErrors() { 381 t.Fatalf("input errors: %s", diags.Err()) 382 } 383 } 384 385 // adding a list interpolation in fails to interpolate the count variable 386 func TestContext2Input_submoduleTriggersInvalidCount(t *testing.T) { 387 input := new(MockUIInput) 388 m := testModule(t, "input-submodule-count") 389 p := testProvider("aws") 390 ctx := testContext2(t, &ContextOpts{ 391 Providers: map[addrs.Provider]providers.Factory{ 392 addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), 393 }, 394 UIInput: input, 395 }) 396 397 if diags := ctx.Input(m, InputModeStd); diags.HasErrors() { 398 t.Fatalf("input errors: %s", diags.Err()) 399 } 400 } 401 402 // In this case, a module variable can't be resolved from a data source until 403 // it's refreshed, but it can't be refreshed during Input. 404 func TestContext2Input_dataSourceRequiresRefresh(t *testing.T) { 405 input := new(MockUIInput) 406 p := testProvider("null") 407 m := testModule(t, "input-module-data-vars") 408 409 p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ 410 DataSources: map[string]*configschema.Block{ 411 "null_data_source": { 412 Attributes: map[string]*configschema.Attribute{ 413 "foo": {Type: cty.List(cty.String), Optional: true}, 414 }, 415 }, 416 }, 417 }) 418 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 419 return providers.ReadDataSourceResponse{ 420 State: req.Config, 421 } 422 } 423 424 state := states.BuildState(func(s *states.SyncState) { 425 s.SetResourceInstanceCurrent( 426 addrs.Resource{ 427 Mode: addrs.DataResourceMode, 428 Type: "null_data_source", 429 Name: "bar", 430 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 431 &states.ResourceInstanceObjectSrc{ 432 AttrsFlat: map[string]string{ 433 "id": "-", 434 "foo.#": "1", 435 "foo.0": "a", 436 // foo.1 exists in the data source, but needs to be refreshed. 437 }, 438 Status: states.ObjectReady, 439 }, 440 addrs.AbsProviderConfig{ 441 Provider: addrs.NewDefaultProvider("null"), 442 Module: addrs.RootModule, 443 }, 444 ) 445 }) 446 447 ctx := testContext2(t, &ContextOpts{ 448 Providers: map[addrs.Provider]providers.Factory{ 449 addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), 450 }, 451 UIInput: input, 452 }) 453 454 if diags := ctx.Input(m, InputModeStd); diags.HasErrors() { 455 t.Fatalf("input errors: %s", diags.Err()) 456 } 457 458 // ensure that plan works after Refresh. This is a legacy test that 459 // doesn't really make sense anymore, because Refresh is really just 460 // a wrapper around plan anyway, but we're keeping it until we get a 461 // chance to review and check whether it's giving us any additional 462 // test coverage aside from what it's specifically intending to test. 463 if _, diags := ctx.Refresh(m, state, DefaultPlanOpts); diags.HasErrors() { 464 t.Fatalf("refresh errors: %s", diags.Err()) 465 } 466 if _, diags := ctx.Plan(m, state, DefaultPlanOpts); diags.HasErrors() { 467 t.Fatalf("plan errors: %s", diags.Err()) 468 } 469 }