github.com/Hashicorp/terraform@v0.11.12-beta1/helper/schema/provider_test.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 "time" 8 9 "github.com/davecgh/go-spew/spew" 10 "github.com/zclconf/go-cty/cty" 11 12 "github.com/hashicorp/terraform/config" 13 "github.com/hashicorp/terraform/config/configschema" 14 "github.com/hashicorp/terraform/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": &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": &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 !reflect.DeepEqual(got, want) { 95 t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) 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, err := config.NewRawConfig(tc.Config) 160 if err != nil { 161 t.Fatalf("err: %s", err) 162 } 163 164 err = tc.P.Configure(terraform.NewResourceConfig(c)) 165 if err != nil != tc.Err { 166 t.Fatalf("%d: %s", i, err) 167 } 168 } 169 } 170 171 func TestProviderResources(t *testing.T) { 172 cases := []struct { 173 P *Provider 174 Result []terraform.ResourceType 175 }{ 176 { 177 P: &Provider{}, 178 Result: []terraform.ResourceType{}, 179 }, 180 181 { 182 P: &Provider{ 183 ResourcesMap: map[string]*Resource{ 184 "foo": nil, 185 "bar": nil, 186 }, 187 }, 188 Result: []terraform.ResourceType{ 189 terraform.ResourceType{Name: "bar", SchemaAvailable: true}, 190 terraform.ResourceType{Name: "foo", SchemaAvailable: true}, 191 }, 192 }, 193 194 { 195 P: &Provider{ 196 ResourcesMap: map[string]*Resource{ 197 "foo": nil, 198 "bar": &Resource{Importer: &ResourceImporter{}}, 199 "baz": nil, 200 }, 201 }, 202 Result: []terraform.ResourceType{ 203 terraform.ResourceType{Name: "bar", Importable: true, SchemaAvailable: true}, 204 terraform.ResourceType{Name: "baz", SchemaAvailable: true}, 205 terraform.ResourceType{Name: "foo", SchemaAvailable: true}, 206 }, 207 }, 208 } 209 210 for i, tc := range cases { 211 actual := tc.P.Resources() 212 if !reflect.DeepEqual(actual, tc.Result) { 213 t.Fatalf("%d: %#v", i, actual) 214 } 215 } 216 } 217 218 func TestProviderDataSources(t *testing.T) { 219 cases := []struct { 220 P *Provider 221 Result []terraform.DataSource 222 }{ 223 { 224 P: &Provider{}, 225 Result: []terraform.DataSource{}, 226 }, 227 228 { 229 P: &Provider{ 230 DataSourcesMap: map[string]*Resource{ 231 "foo": nil, 232 "bar": nil, 233 }, 234 }, 235 Result: []terraform.DataSource{ 236 terraform.DataSource{Name: "bar", SchemaAvailable: true}, 237 terraform.DataSource{Name: "foo", SchemaAvailable: true}, 238 }, 239 }, 240 } 241 242 for i, tc := range cases { 243 actual := tc.P.DataSources() 244 if !reflect.DeepEqual(actual, tc.Result) { 245 t.Fatalf("%d: got %#v; want %#v", i, actual, tc.Result) 246 } 247 } 248 } 249 250 func TestProviderValidate(t *testing.T) { 251 cases := []struct { 252 P *Provider 253 Config map[string]interface{} 254 Err bool 255 }{ 256 { 257 P: &Provider{ 258 Schema: map[string]*Schema{ 259 "foo": &Schema{}, 260 }, 261 }, 262 Config: nil, 263 Err: true, 264 }, 265 } 266 267 for i, tc := range cases { 268 c, err := config.NewRawConfig(tc.Config) 269 if err != nil { 270 t.Fatalf("err: %s", err) 271 } 272 273 _, es := tc.P.Validate(terraform.NewResourceConfig(c)) 274 if len(es) > 0 != tc.Err { 275 t.Fatalf("%d: %#v", i, es) 276 } 277 } 278 } 279 280 func TestProviderValidateResource(t *testing.T) { 281 cases := []struct { 282 P *Provider 283 Type string 284 Config map[string]interface{} 285 Err bool 286 }{ 287 { 288 P: &Provider{}, 289 Type: "foo", 290 Config: nil, 291 Err: true, 292 }, 293 294 { 295 P: &Provider{ 296 ResourcesMap: map[string]*Resource{ 297 "foo": &Resource{}, 298 }, 299 }, 300 Type: "foo", 301 Config: nil, 302 Err: false, 303 }, 304 } 305 306 for i, tc := range cases { 307 c, err := config.NewRawConfig(tc.Config) 308 if err != nil { 309 t.Fatalf("err: %s", err) 310 } 311 312 _, es := tc.P.ValidateResource(tc.Type, terraform.NewResourceConfig(c)) 313 if len(es) > 0 != tc.Err { 314 t.Fatalf("%d: %#v", i, es) 315 } 316 } 317 } 318 319 func TestProviderImportState_default(t *testing.T) { 320 p := &Provider{ 321 ResourcesMap: map[string]*Resource{ 322 "foo": &Resource{ 323 Importer: &ResourceImporter{}, 324 }, 325 }, 326 } 327 328 states, err := p.ImportState(&terraform.InstanceInfo{ 329 Type: "foo", 330 }, "bar") 331 if err != nil { 332 t.Fatalf("err: %s", err) 333 } 334 335 if len(states) != 1 { 336 t.Fatalf("bad: %#v", states) 337 } 338 if states[0].ID != "bar" { 339 t.Fatalf("bad: %#v", states) 340 } 341 } 342 343 func TestProviderImportState_setsId(t *testing.T) { 344 var val string 345 stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { 346 val = d.Id() 347 return []*ResourceData{d}, nil 348 } 349 350 p := &Provider{ 351 ResourcesMap: map[string]*Resource{ 352 "foo": &Resource{ 353 Importer: &ResourceImporter{ 354 State: stateFunc, 355 }, 356 }, 357 }, 358 } 359 360 _, err := p.ImportState(&terraform.InstanceInfo{ 361 Type: "foo", 362 }, "bar") 363 if err != nil { 364 t.Fatalf("err: %s", err) 365 } 366 367 if val != "bar" { 368 t.Fatal("should set id") 369 } 370 } 371 372 func TestProviderImportState_setsType(t *testing.T) { 373 var tVal string 374 stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { 375 d.SetId("foo") 376 tVal = d.State().Ephemeral.Type 377 return []*ResourceData{d}, nil 378 } 379 380 p := &Provider{ 381 ResourcesMap: map[string]*Resource{ 382 "foo": &Resource{ 383 Importer: &ResourceImporter{ 384 State: stateFunc, 385 }, 386 }, 387 }, 388 } 389 390 _, err := p.ImportState(&terraform.InstanceInfo{ 391 Type: "foo", 392 }, "bar") 393 if err != nil { 394 t.Fatalf("err: %s", err) 395 } 396 397 if tVal != "foo" { 398 t.Fatal("should set type") 399 } 400 } 401 402 func TestProviderMeta(t *testing.T) { 403 p := new(Provider) 404 if v := p.Meta(); v != nil { 405 t.Fatalf("bad: %#v", v) 406 } 407 408 expected := 42 409 p.SetMeta(42) 410 if v := p.Meta(); !reflect.DeepEqual(v, expected) { 411 t.Fatalf("bad: %#v", v) 412 } 413 } 414 415 func TestProviderStop(t *testing.T) { 416 var p Provider 417 418 if p.Stopped() { 419 t.Fatal("should not be stopped") 420 } 421 422 // Verify stopch blocks 423 ch := p.StopContext().Done() 424 select { 425 case <-ch: 426 t.Fatal("should not be stopped") 427 case <-time.After(10 * time.Millisecond): 428 } 429 430 // Stop it 431 if err := p.Stop(); err != nil { 432 t.Fatalf("err: %s", err) 433 } 434 435 // Verify 436 if !p.Stopped() { 437 t.Fatal("should be stopped") 438 } 439 440 select { 441 case <-ch: 442 case <-time.After(10 * time.Millisecond): 443 t.Fatal("should be stopped") 444 } 445 } 446 447 func TestProviderStop_stopFirst(t *testing.T) { 448 var p Provider 449 450 // Stop it 451 if err := p.Stop(); err != nil { 452 t.Fatalf("err: %s", err) 453 } 454 455 // Verify 456 if !p.Stopped() { 457 t.Fatal("should be stopped") 458 } 459 460 select { 461 case <-p.StopContext().Done(): 462 case <-time.After(10 * time.Millisecond): 463 t.Fatal("should be stopped") 464 } 465 } 466 467 func TestProviderReset(t *testing.T) { 468 var p Provider 469 stopCtx := p.StopContext() 470 p.MetaReset = func() error { 471 stopCtx = p.StopContext() 472 return nil 473 } 474 475 // cancel the current context 476 p.Stop() 477 478 if err := p.TestReset(); err != nil { 479 t.Fatal(err) 480 } 481 482 // the first context should have been replaced 483 if err := stopCtx.Err(); err != nil { 484 t.Fatal(err) 485 } 486 487 // we should not get a canceled context here either 488 if err := p.StopContext().Err(); err != nil { 489 t.Fatal(err) 490 } 491 } 492 493 func TestProvider_InternalValidate(t *testing.T) { 494 cases := []struct { 495 P *Provider 496 ExpectedErr error 497 }{ 498 { 499 P: &Provider{ 500 Schema: map[string]*Schema{ 501 "foo": { 502 Type: TypeBool, 503 Optional: true, 504 }, 505 }, 506 }, 507 ExpectedErr: nil, 508 }, 509 { // Reserved resource fields should be allowed in provider block 510 P: &Provider{ 511 Schema: map[string]*Schema{ 512 "provisioner": { 513 Type: TypeString, 514 Optional: true, 515 }, 516 "count": { 517 Type: TypeInt, 518 Optional: true, 519 }, 520 }, 521 }, 522 ExpectedErr: nil, 523 }, 524 { // Reserved provider fields should not be allowed 525 P: &Provider{ 526 Schema: map[string]*Schema{ 527 "alias": { 528 Type: TypeString, 529 Optional: true, 530 }, 531 }, 532 }, 533 ExpectedErr: fmt.Errorf("%s is a reserved field name for a provider", "alias"), 534 }, 535 } 536 537 for i, tc := range cases { 538 err := tc.P.InternalValidate() 539 if tc.ExpectedErr == nil { 540 if err != nil { 541 t.Fatalf("%d: Error returned (expected no error): %s", i, err) 542 } 543 continue 544 } 545 if tc.ExpectedErr != nil && err == nil { 546 t.Fatalf("%d: Expected error (%s), but no error returned", i, tc.ExpectedErr) 547 } 548 if err.Error() != tc.ExpectedErr.Error() { 549 t.Fatalf("%d: Errors don't match. Expected: %#v Given: %#v", i, tc.ExpectedErr, err) 550 } 551 } 552 }