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  }