github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/legacy/helper/schema/provider_test.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/google/go-cmp/cmp" 11 "github.com/zclconf/go-cty/cty" 12 13 "github.com/hashicorp/terraform/internal/configs/configschema" 14 "github.com/hashicorp/terraform/internal/legacy/terraform" 15 ) 16 17 func TestProvider_impl(t *testing.T) { 18 var _ terraform.ResourceProvider = new(Provider) 19 } 20 21 func TestProviderGetSchema(t *testing.T) { 22 // This functionality is already broadly tested in core_schema_test.go, 23 // so this is just to ensure that the call passes through correctly. 24 p := &Provider{ 25 Schema: map[string]*Schema{ 26 "bar": { 27 Type: TypeString, 28 Required: true, 29 }, 30 }, 31 ResourcesMap: map[string]*Resource{ 32 "foo": &Resource{ 33 Schema: map[string]*Schema{ 34 "bar": { 35 Type: TypeString, 36 Required: true, 37 }, 38 }, 39 }, 40 }, 41 DataSourcesMap: map[string]*Resource{ 42 "baz": &Resource{ 43 Schema: map[string]*Schema{ 44 "bur": { 45 Type: TypeString, 46 Required: true, 47 }, 48 }, 49 }, 50 }, 51 } 52 53 want := &terraform.ProviderSchema{ 54 Provider: &configschema.Block{ 55 Attributes: map[string]*configschema.Attribute{ 56 "bar": &configschema.Attribute{ 57 Type: cty.String, 58 Required: true, 59 }, 60 }, 61 BlockTypes: map[string]*configschema.NestedBlock{}, 62 }, 63 ResourceTypes: map[string]*configschema.Block{ 64 "foo": testResource(&configschema.Block{ 65 Attributes: map[string]*configschema.Attribute{ 66 "bar": &configschema.Attribute{ 67 Type: cty.String, 68 Required: true, 69 }, 70 }, 71 BlockTypes: map[string]*configschema.NestedBlock{}, 72 }), 73 }, 74 DataSources: map[string]*configschema.Block{ 75 "baz": testResource(&configschema.Block{ 76 Attributes: map[string]*configschema.Attribute{ 77 "bur": &configschema.Attribute{ 78 Type: cty.String, 79 Required: true, 80 }, 81 }, 82 BlockTypes: map[string]*configschema.NestedBlock{}, 83 }), 84 }, 85 } 86 got, err := p.GetSchema(&terraform.ProviderSchemaRequest{ 87 ResourceTypes: []string{"foo", "bar"}, 88 DataSources: []string{"baz", "bar"}, 89 }) 90 if err != nil { 91 t.Fatalf("unexpected error %s", err) 92 } 93 94 if !cmp.Equal(got, want, equateEmpty, typeComparer) { 95 t.Error("wrong result:\n", cmp.Diff(got, want, equateEmpty, typeComparer)) 96 } 97 } 98 99 func TestProviderConfigure(t *testing.T) { 100 cases := []struct { 101 P *Provider 102 Config map[string]interface{} 103 Err bool 104 }{ 105 { 106 P: &Provider{}, 107 Config: nil, 108 Err: false, 109 }, 110 111 { 112 P: &Provider{ 113 Schema: map[string]*Schema{ 114 "foo": &Schema{ 115 Type: TypeInt, 116 Optional: true, 117 }, 118 }, 119 120 ConfigureFunc: func(d *ResourceData) (interface{}, error) { 121 if d.Get("foo").(int) == 42 { 122 return nil, nil 123 } 124 125 return nil, fmt.Errorf("nope") 126 }, 127 }, 128 Config: map[string]interface{}{ 129 "foo": 42, 130 }, 131 Err: false, 132 }, 133 134 { 135 P: &Provider{ 136 Schema: map[string]*Schema{ 137 "foo": &Schema{ 138 Type: TypeInt, 139 Optional: true, 140 }, 141 }, 142 143 ConfigureFunc: func(d *ResourceData) (interface{}, error) { 144 if d.Get("foo").(int) == 42 { 145 return nil, nil 146 } 147 148 return nil, fmt.Errorf("nope") 149 }, 150 }, 151 Config: map[string]interface{}{ 152 "foo": 52, 153 }, 154 Err: true, 155 }, 156 } 157 158 for i, tc := range cases { 159 c := terraform.NewResourceConfigRaw(tc.Config) 160 err := tc.P.Configure(c) 161 if err != nil != tc.Err { 162 t.Fatalf("%d: %s", i, err) 163 } 164 } 165 } 166 167 func TestProviderResources(t *testing.T) { 168 cases := []struct { 169 P *Provider 170 Result []terraform.ResourceType 171 }{ 172 { 173 P: &Provider{}, 174 Result: []terraform.ResourceType{}, 175 }, 176 177 { 178 P: &Provider{ 179 ResourcesMap: map[string]*Resource{ 180 "foo": nil, 181 "bar": nil, 182 }, 183 }, 184 Result: []terraform.ResourceType{ 185 terraform.ResourceType{Name: "bar", SchemaAvailable: true}, 186 terraform.ResourceType{Name: "foo", SchemaAvailable: true}, 187 }, 188 }, 189 190 { 191 P: &Provider{ 192 ResourcesMap: map[string]*Resource{ 193 "foo": nil, 194 "bar": &Resource{Importer: &ResourceImporter{}}, 195 "baz": nil, 196 }, 197 }, 198 Result: []terraform.ResourceType{ 199 terraform.ResourceType{Name: "bar", Importable: true, SchemaAvailable: true}, 200 terraform.ResourceType{Name: "baz", SchemaAvailable: true}, 201 terraform.ResourceType{Name: "foo", SchemaAvailable: true}, 202 }, 203 }, 204 } 205 206 for i, tc := range cases { 207 actual := tc.P.Resources() 208 if !reflect.DeepEqual(actual, tc.Result) { 209 t.Fatalf("%d: %#v", i, actual) 210 } 211 } 212 } 213 214 func TestProviderDataSources(t *testing.T) { 215 cases := []struct { 216 P *Provider 217 Result []terraform.DataSource 218 }{ 219 { 220 P: &Provider{}, 221 Result: []terraform.DataSource{}, 222 }, 223 224 { 225 P: &Provider{ 226 DataSourcesMap: map[string]*Resource{ 227 "foo": nil, 228 "bar": nil, 229 }, 230 }, 231 Result: []terraform.DataSource{ 232 terraform.DataSource{Name: "bar", SchemaAvailable: true}, 233 terraform.DataSource{Name: "foo", SchemaAvailable: true}, 234 }, 235 }, 236 } 237 238 for i, tc := range cases { 239 actual := tc.P.DataSources() 240 if !reflect.DeepEqual(actual, tc.Result) { 241 t.Fatalf("%d: got %#v; want %#v", i, actual, tc.Result) 242 } 243 } 244 } 245 246 func TestProviderValidate(t *testing.T) { 247 cases := []struct { 248 P *Provider 249 Config map[string]interface{} 250 Err bool 251 }{ 252 { 253 P: &Provider{ 254 Schema: map[string]*Schema{ 255 "foo": &Schema{}, 256 }, 257 }, 258 Config: nil, 259 Err: true, 260 }, 261 } 262 263 for i, tc := range cases { 264 c := terraform.NewResourceConfigRaw(tc.Config) 265 _, es := tc.P.Validate(c) 266 if len(es) > 0 != tc.Err { 267 t.Fatalf("%d: %#v", i, es) 268 } 269 } 270 } 271 272 func TestProviderDiff_legacyTimeoutType(t *testing.T) { 273 p := &Provider{ 274 ResourcesMap: map[string]*Resource{ 275 "blah": &Resource{ 276 Schema: map[string]*Schema{ 277 "foo": { 278 Type: TypeInt, 279 Optional: true, 280 }, 281 }, 282 Timeouts: &ResourceTimeout{ 283 Create: DefaultTimeout(10 * time.Minute), 284 }, 285 }, 286 }, 287 } 288 289 invalidCfg := map[string]interface{}{ 290 "foo": 42, 291 "timeouts": []interface{}{ 292 map[string]interface{}{ 293 "create": "40m", 294 }, 295 }, 296 } 297 ic := terraform.NewResourceConfigRaw(invalidCfg) 298 _, err := p.Diff( 299 &terraform.InstanceInfo{ 300 Type: "blah", 301 }, 302 nil, 303 ic, 304 ) 305 if err != nil { 306 t.Fatal(err) 307 } 308 } 309 310 func TestProviderDiff_timeoutInvalidValue(t *testing.T) { 311 p := &Provider{ 312 ResourcesMap: map[string]*Resource{ 313 "blah": &Resource{ 314 Schema: map[string]*Schema{ 315 "foo": { 316 Type: TypeInt, 317 Optional: true, 318 }, 319 }, 320 Timeouts: &ResourceTimeout{ 321 Create: DefaultTimeout(10 * time.Minute), 322 }, 323 }, 324 }, 325 } 326 327 invalidCfg := map[string]interface{}{ 328 "foo": 42, 329 "timeouts": map[string]interface{}{ 330 "create": "invalid", 331 }, 332 } 333 ic := terraform.NewResourceConfigRaw(invalidCfg) 334 _, err := p.Diff( 335 &terraform.InstanceInfo{ 336 Type: "blah", 337 }, 338 nil, 339 ic, 340 ) 341 if err == nil { 342 t.Fatal("Expected provider.Diff to fail with invalid timeout value") 343 } 344 expectedErrMsg := `time: invalid duration "invalid"` 345 if !strings.Contains(err.Error(), expectedErrMsg) { 346 t.Fatalf("Unexpected error message: %q\nExpected message to contain %q", 347 err.Error(), 348 expectedErrMsg) 349 } 350 } 351 352 func TestProviderValidateResource(t *testing.T) { 353 cases := []struct { 354 P *Provider 355 Type string 356 Config map[string]interface{} 357 Err bool 358 }{ 359 { 360 P: &Provider{}, 361 Type: "foo", 362 Config: nil, 363 Err: true, 364 }, 365 366 { 367 P: &Provider{ 368 ResourcesMap: map[string]*Resource{ 369 "foo": &Resource{}, 370 }, 371 }, 372 Type: "foo", 373 Config: nil, 374 Err: false, 375 }, 376 } 377 378 for i, tc := range cases { 379 c := terraform.NewResourceConfigRaw(tc.Config) 380 _, es := tc.P.ValidateResource(tc.Type, c) 381 if len(es) > 0 != tc.Err { 382 t.Fatalf("%d: %#v", i, es) 383 } 384 } 385 } 386 387 func TestProviderImportState_default(t *testing.T) { 388 p := &Provider{ 389 ResourcesMap: map[string]*Resource{ 390 "foo": &Resource{ 391 Importer: &ResourceImporter{}, 392 }, 393 }, 394 } 395 396 states, err := p.ImportState(&terraform.InstanceInfo{ 397 Type: "foo", 398 }, "bar") 399 if err != nil { 400 t.Fatalf("err: %s", err) 401 } 402 403 if len(states) != 1 { 404 t.Fatalf("bad: %#v", states) 405 } 406 if states[0].ID != "bar" { 407 t.Fatalf("bad: %#v", states) 408 } 409 } 410 411 func TestProviderImportState_setsId(t *testing.T) { 412 var val string 413 stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { 414 val = d.Id() 415 return []*ResourceData{d}, nil 416 } 417 418 p := &Provider{ 419 ResourcesMap: map[string]*Resource{ 420 "foo": &Resource{ 421 Importer: &ResourceImporter{ 422 State: stateFunc, 423 }, 424 }, 425 }, 426 } 427 428 _, err := p.ImportState(&terraform.InstanceInfo{ 429 Type: "foo", 430 }, "bar") 431 if err != nil { 432 t.Fatalf("err: %s", err) 433 } 434 435 if val != "bar" { 436 t.Fatal("should set id") 437 } 438 } 439 440 func TestProviderImportState_setsType(t *testing.T) { 441 var tVal string 442 stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { 443 d.SetId("foo") 444 tVal = d.State().Ephemeral.Type 445 return []*ResourceData{d}, nil 446 } 447 448 p := &Provider{ 449 ResourcesMap: map[string]*Resource{ 450 "foo": &Resource{ 451 Importer: &ResourceImporter{ 452 State: stateFunc, 453 }, 454 }, 455 }, 456 } 457 458 _, err := p.ImportState(&terraform.InstanceInfo{ 459 Type: "foo", 460 }, "bar") 461 if err != nil { 462 t.Fatalf("err: %s", err) 463 } 464 465 if tVal != "foo" { 466 t.Fatal("should set type") 467 } 468 } 469 470 func TestProviderMeta(t *testing.T) { 471 p := new(Provider) 472 if v := p.Meta(); v != nil { 473 t.Fatalf("bad: %#v", v) 474 } 475 476 expected := 42 477 p.SetMeta(42) 478 if v := p.Meta(); !reflect.DeepEqual(v, expected) { 479 t.Fatalf("bad: %#v", v) 480 } 481 } 482 483 func TestProviderStop(t *testing.T) { 484 var p Provider 485 486 if p.Stopped() { 487 t.Fatal("should not be stopped") 488 } 489 490 // Verify stopch blocks 491 ch := p.StopContext().Done() 492 select { 493 case <-ch: 494 t.Fatal("should not be stopped") 495 case <-time.After(10 * time.Millisecond): 496 } 497 498 // Stop it 499 if err := p.Stop(); err != nil { 500 t.Fatalf("err: %s", err) 501 } 502 503 // Verify 504 if !p.Stopped() { 505 t.Fatal("should be stopped") 506 } 507 508 select { 509 case <-ch: 510 case <-time.After(10 * time.Millisecond): 511 t.Fatal("should be stopped") 512 } 513 } 514 515 func TestProviderStop_stopFirst(t *testing.T) { 516 var p Provider 517 518 // Stop it 519 if err := p.Stop(); err != nil { 520 t.Fatalf("err: %s", err) 521 } 522 523 // Verify 524 if !p.Stopped() { 525 t.Fatal("should be stopped") 526 } 527 528 select { 529 case <-p.StopContext().Done(): 530 case <-time.After(10 * time.Millisecond): 531 t.Fatal("should be stopped") 532 } 533 } 534 535 func TestProviderReset(t *testing.T) { 536 var p Provider 537 stopCtx := p.StopContext() 538 p.MetaReset = func() error { 539 stopCtx = p.StopContext() 540 return nil 541 } 542 543 // cancel the current context 544 p.Stop() 545 546 if err := p.TestReset(); err != nil { 547 t.Fatal(err) 548 } 549 550 // the first context should have been replaced 551 if err := stopCtx.Err(); err != nil { 552 t.Fatal(err) 553 } 554 555 // we should not get a canceled context here either 556 if err := p.StopContext().Err(); err != nil { 557 t.Fatal(err) 558 } 559 } 560 561 func TestProvider_InternalValidate(t *testing.T) { 562 cases := []struct { 563 P *Provider 564 ExpectedErr error 565 }{ 566 { 567 P: &Provider{ 568 Schema: map[string]*Schema{ 569 "foo": { 570 Type: TypeBool, 571 Optional: true, 572 }, 573 }, 574 }, 575 ExpectedErr: nil, 576 }, 577 { // Reserved resource fields should be allowed in provider block 578 P: &Provider{ 579 Schema: map[string]*Schema{ 580 "provisioner": { 581 Type: TypeString, 582 Optional: true, 583 }, 584 "count": { 585 Type: TypeInt, 586 Optional: true, 587 }, 588 }, 589 }, 590 ExpectedErr: nil, 591 }, 592 { // Reserved provider fields should not be allowed 593 P: &Provider{ 594 Schema: map[string]*Schema{ 595 "alias": { 596 Type: TypeString, 597 Optional: true, 598 }, 599 }, 600 }, 601 ExpectedErr: fmt.Errorf("%s is a reserved field name for a provider", "alias"), 602 }, 603 } 604 605 for i, tc := range cases { 606 err := tc.P.InternalValidate() 607 if tc.ExpectedErr == nil { 608 if err != nil { 609 t.Fatalf("%d: Error returned (expected no error): %s", i, err) 610 } 611 continue 612 } 613 if tc.ExpectedErr != nil && err == nil { 614 t.Fatalf("%d: Expected error (%s), but no error returned", i, tc.ExpectedErr) 615 } 616 if err.Error() != tc.ExpectedErr.Error() { 617 t.Fatalf("%d: Errors don't match. Expected: %#v Given: %#v", i, tc.ExpectedErr, err) 618 } 619 } 620 }