github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/helper/schema/resource_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/terraform/terraform"
    10  )
    11  
    12  func TestResourceApply_create(t *testing.T) {
    13  	r := &Resource{
    14  		SchemaVersion: 2,
    15  		Schema: map[string]*Schema{
    16  			"foo": &Schema{
    17  				Type:     TypeInt,
    18  				Optional: true,
    19  			},
    20  		},
    21  	}
    22  
    23  	called := false
    24  	r.Create = func(d *ResourceData, m interface{}) error {
    25  		called = true
    26  		d.SetId("foo")
    27  		return nil
    28  	}
    29  
    30  	var s *terraform.InstanceState = nil
    31  
    32  	d := &terraform.InstanceDiff{
    33  		Attributes: map[string]*terraform.ResourceAttrDiff{
    34  			"foo": &terraform.ResourceAttrDiff{
    35  				New: "42",
    36  			},
    37  		},
    38  	}
    39  
    40  	actual, err := r.Apply(s, d, nil)
    41  	if err != nil {
    42  		t.Fatalf("err: %s", err)
    43  	}
    44  
    45  	if !called {
    46  		t.Fatal("not called")
    47  	}
    48  
    49  	expected := &terraform.InstanceState{
    50  		ID: "foo",
    51  		Attributes: map[string]string{
    52  			"id":  "foo",
    53  			"foo": "42",
    54  		},
    55  		Meta: map[string]string{
    56  			"schema_version": "2",
    57  		},
    58  	}
    59  
    60  	if !reflect.DeepEqual(actual, expected) {
    61  		t.Fatalf("bad: %#v", actual)
    62  	}
    63  }
    64  
    65  func TestResourceApply_destroy(t *testing.T) {
    66  	r := &Resource{
    67  		Schema: map[string]*Schema{
    68  			"foo": &Schema{
    69  				Type:     TypeInt,
    70  				Optional: true,
    71  			},
    72  		},
    73  	}
    74  
    75  	called := false
    76  	r.Delete = func(d *ResourceData, m interface{}) error {
    77  		called = true
    78  		return nil
    79  	}
    80  
    81  	s := &terraform.InstanceState{
    82  		ID: "bar",
    83  	}
    84  
    85  	d := &terraform.InstanceDiff{
    86  		Destroy: true,
    87  	}
    88  
    89  	actual, err := r.Apply(s, d, nil)
    90  	if err != nil {
    91  		t.Fatalf("err: %s", err)
    92  	}
    93  
    94  	if !called {
    95  		t.Fatal("delete not called")
    96  	}
    97  
    98  	if actual != nil {
    99  		t.Fatalf("bad: %#v", actual)
   100  	}
   101  }
   102  
   103  func TestResourceApply_destroyCreate(t *testing.T) {
   104  	r := &Resource{
   105  		Schema: map[string]*Schema{
   106  			"foo": &Schema{
   107  				Type:     TypeInt,
   108  				Optional: true,
   109  			},
   110  
   111  			"tags": &Schema{
   112  				Type:     TypeMap,
   113  				Optional: true,
   114  				Computed: true,
   115  			},
   116  		},
   117  	}
   118  
   119  	change := false
   120  	r.Create = func(d *ResourceData, m interface{}) error {
   121  		change = d.HasChange("tags")
   122  		d.SetId("foo")
   123  		return nil
   124  	}
   125  	r.Delete = func(d *ResourceData, m interface{}) error {
   126  		return nil
   127  	}
   128  
   129  	var s *terraform.InstanceState = &terraform.InstanceState{
   130  		ID: "bar",
   131  		Attributes: map[string]string{
   132  			"foo":       "bar",
   133  			"tags.Name": "foo",
   134  		},
   135  	}
   136  
   137  	d := &terraform.InstanceDiff{
   138  		Attributes: map[string]*terraform.ResourceAttrDiff{
   139  			"foo": &terraform.ResourceAttrDiff{
   140  				New:         "42",
   141  				RequiresNew: true,
   142  			},
   143  			"tags.Name": &terraform.ResourceAttrDiff{
   144  				Old:         "foo",
   145  				New:         "foo",
   146  				RequiresNew: true,
   147  			},
   148  		},
   149  	}
   150  
   151  	actual, err := r.Apply(s, d, nil)
   152  	if err != nil {
   153  		t.Fatalf("err: %s", err)
   154  	}
   155  
   156  	if !change {
   157  		t.Fatal("should have change")
   158  	}
   159  
   160  	expected := &terraform.InstanceState{
   161  		ID: "foo",
   162  		Attributes: map[string]string{
   163  			"id":        "foo",
   164  			"foo":       "42",
   165  			"tags.#":    "1",
   166  			"tags.Name": "foo",
   167  		},
   168  	}
   169  
   170  	if !reflect.DeepEqual(actual, expected) {
   171  		t.Fatalf("bad: %#v", actual)
   172  	}
   173  }
   174  
   175  func TestResourceApply_destroyPartial(t *testing.T) {
   176  	r := &Resource{
   177  		Schema: map[string]*Schema{
   178  			"foo": &Schema{
   179  				Type:     TypeInt,
   180  				Optional: true,
   181  			},
   182  		},
   183  	}
   184  
   185  	r.Delete = func(d *ResourceData, m interface{}) error {
   186  		d.Set("foo", 42)
   187  		return fmt.Errorf("some error")
   188  	}
   189  
   190  	s := &terraform.InstanceState{
   191  		ID: "bar",
   192  		Attributes: map[string]string{
   193  			"foo": "12",
   194  		},
   195  	}
   196  
   197  	d := &terraform.InstanceDiff{
   198  		Destroy: true,
   199  	}
   200  
   201  	actual, err := r.Apply(s, d, nil)
   202  	if err == nil {
   203  		t.Fatal("should error")
   204  	}
   205  
   206  	expected := &terraform.InstanceState{
   207  		ID: "bar",
   208  		Attributes: map[string]string{
   209  			"id":  "bar",
   210  			"foo": "42",
   211  		},
   212  	}
   213  
   214  	if !reflect.DeepEqual(actual, expected) {
   215  		t.Fatalf("bad: %#v", actual)
   216  	}
   217  }
   218  
   219  func TestResourceApply_update(t *testing.T) {
   220  	r := &Resource{
   221  		Schema: map[string]*Schema{
   222  			"foo": &Schema{
   223  				Type:     TypeInt,
   224  				Optional: true,
   225  			},
   226  		},
   227  	}
   228  
   229  	r.Update = func(d *ResourceData, m interface{}) error {
   230  		d.Set("foo", 42)
   231  		return nil
   232  	}
   233  
   234  	s := &terraform.InstanceState{
   235  		ID: "foo",
   236  		Attributes: map[string]string{
   237  			"foo": "12",
   238  		},
   239  	}
   240  
   241  	d := &terraform.InstanceDiff{
   242  		Attributes: map[string]*terraform.ResourceAttrDiff{
   243  			"foo": &terraform.ResourceAttrDiff{
   244  				New: "13",
   245  			},
   246  		},
   247  	}
   248  
   249  	actual, err := r.Apply(s, d, nil)
   250  	if err != nil {
   251  		t.Fatalf("err: %s", err)
   252  	}
   253  
   254  	expected := &terraform.InstanceState{
   255  		ID: "foo",
   256  		Attributes: map[string]string{
   257  			"id":  "foo",
   258  			"foo": "42",
   259  		},
   260  	}
   261  
   262  	if !reflect.DeepEqual(actual, expected) {
   263  		t.Fatalf("bad: %#v", actual)
   264  	}
   265  }
   266  
   267  func TestResourceApply_updateNoCallback(t *testing.T) {
   268  	r := &Resource{
   269  		Schema: map[string]*Schema{
   270  			"foo": &Schema{
   271  				Type:     TypeInt,
   272  				Optional: true,
   273  			},
   274  		},
   275  	}
   276  
   277  	r.Update = nil
   278  
   279  	s := &terraform.InstanceState{
   280  		ID: "foo",
   281  		Attributes: map[string]string{
   282  			"foo": "12",
   283  		},
   284  	}
   285  
   286  	d := &terraform.InstanceDiff{
   287  		Attributes: map[string]*terraform.ResourceAttrDiff{
   288  			"foo": &terraform.ResourceAttrDiff{
   289  				New: "13",
   290  			},
   291  		},
   292  	}
   293  
   294  	actual, err := r.Apply(s, d, nil)
   295  	if err == nil {
   296  		t.Fatal("should error")
   297  	}
   298  
   299  	expected := &terraform.InstanceState{
   300  		ID: "foo",
   301  		Attributes: map[string]string{
   302  			"foo": "12",
   303  		},
   304  	}
   305  
   306  	if !reflect.DeepEqual(actual, expected) {
   307  		t.Fatalf("bad: %#v", actual)
   308  	}
   309  }
   310  
   311  func TestResourceInternalValidate(t *testing.T) {
   312  	cases := []struct {
   313  		In  *Resource
   314  		Err bool
   315  	}{
   316  		{
   317  			nil,
   318  			true,
   319  		},
   320  
   321  		// No optional and no required
   322  		{
   323  			&Resource{
   324  				Schema: map[string]*Schema{
   325  					"foo": &Schema{
   326  						Type:     TypeInt,
   327  						Optional: true,
   328  						Required: true,
   329  					},
   330  				},
   331  			},
   332  			true,
   333  		},
   334  	}
   335  
   336  	for i, tc := range cases {
   337  		err := tc.In.InternalValidate()
   338  		if (err != nil) != tc.Err {
   339  			t.Fatalf("%d: bad: %s", i, err)
   340  		}
   341  	}
   342  }
   343  
   344  func TestResourceRefresh(t *testing.T) {
   345  	r := &Resource{
   346  		SchemaVersion: 2,
   347  		Schema: map[string]*Schema{
   348  			"foo": &Schema{
   349  				Type:     TypeInt,
   350  				Optional: true,
   351  			},
   352  		},
   353  	}
   354  
   355  	r.Read = func(d *ResourceData, m interface{}) error {
   356  		if m != 42 {
   357  			return fmt.Errorf("meta not passed")
   358  		}
   359  
   360  		return d.Set("foo", d.Get("foo").(int)+1)
   361  	}
   362  
   363  	s := &terraform.InstanceState{
   364  		ID: "bar",
   365  		Attributes: map[string]string{
   366  			"foo": "12",
   367  		},
   368  	}
   369  
   370  	expected := &terraform.InstanceState{
   371  		ID: "bar",
   372  		Attributes: map[string]string{
   373  			"id":  "bar",
   374  			"foo": "13",
   375  		},
   376  		Meta: map[string]string{
   377  			"schema_version": "2",
   378  		},
   379  	}
   380  
   381  	actual, err := r.Refresh(s, 42)
   382  	if err != nil {
   383  		t.Fatalf("err: %s", err)
   384  	}
   385  
   386  	if !reflect.DeepEqual(actual, expected) {
   387  		t.Fatalf("bad: %#v", actual)
   388  	}
   389  }
   390  
   391  func TestResourceRefresh_delete(t *testing.T) {
   392  	r := &Resource{
   393  		Schema: map[string]*Schema{
   394  			"foo": &Schema{
   395  				Type:     TypeInt,
   396  				Optional: true,
   397  			},
   398  		},
   399  	}
   400  
   401  	r.Read = func(d *ResourceData, m interface{}) error {
   402  		d.SetId("")
   403  		return nil
   404  	}
   405  
   406  	s := &terraform.InstanceState{
   407  		ID: "bar",
   408  		Attributes: map[string]string{
   409  			"foo": "12",
   410  		},
   411  	}
   412  
   413  	actual, err := r.Refresh(s, 42)
   414  	if err != nil {
   415  		t.Fatalf("err: %s", err)
   416  	}
   417  
   418  	if actual != nil {
   419  		t.Fatalf("bad: %#v", actual)
   420  	}
   421  }
   422  
   423  func TestResourceRefresh_existsError(t *testing.T) {
   424  	r := &Resource{
   425  		Schema: map[string]*Schema{
   426  			"foo": &Schema{
   427  				Type:     TypeInt,
   428  				Optional: true,
   429  			},
   430  		},
   431  	}
   432  
   433  	r.Exists = func(*ResourceData, interface{}) (bool, error) {
   434  		return false, fmt.Errorf("error")
   435  	}
   436  
   437  	r.Read = func(d *ResourceData, m interface{}) error {
   438  		panic("shouldn't be called")
   439  	}
   440  
   441  	s := &terraform.InstanceState{
   442  		ID: "bar",
   443  		Attributes: map[string]string{
   444  			"foo": "12",
   445  		},
   446  	}
   447  
   448  	actual, err := r.Refresh(s, 42)
   449  	if err == nil {
   450  		t.Fatalf("should error")
   451  	}
   452  	if !reflect.DeepEqual(actual, s) {
   453  		t.Fatalf("bad: %#v", actual)
   454  	}
   455  }
   456  
   457  func TestResourceRefresh_noExists(t *testing.T) {
   458  	r := &Resource{
   459  		Schema: map[string]*Schema{
   460  			"foo": &Schema{
   461  				Type:     TypeInt,
   462  				Optional: true,
   463  			},
   464  		},
   465  	}
   466  
   467  	r.Exists = func(*ResourceData, interface{}) (bool, error) {
   468  		return false, nil
   469  	}
   470  
   471  	r.Read = func(d *ResourceData, m interface{}) error {
   472  		panic("shouldn't be called")
   473  	}
   474  
   475  	s := &terraform.InstanceState{
   476  		ID: "bar",
   477  		Attributes: map[string]string{
   478  			"foo": "12",
   479  		},
   480  	}
   481  
   482  	actual, err := r.Refresh(s, 42)
   483  	if err != nil {
   484  		t.Fatalf("err: %s", err)
   485  	}
   486  	if actual != nil {
   487  		t.Fatalf("should have no state")
   488  	}
   489  }
   490  
   491  func TestResourceRefresh_needsMigration(t *testing.T) {
   492  	// Schema v2 it deals only in newfoo, which tracks foo as an int
   493  	r := &Resource{
   494  		SchemaVersion: 2,
   495  		Schema: map[string]*Schema{
   496  			"newfoo": &Schema{
   497  				Type:     TypeInt,
   498  				Optional: true,
   499  			},
   500  		},
   501  	}
   502  
   503  	r.Read = func(d *ResourceData, m interface{}) error {
   504  		return d.Set("newfoo", d.Get("newfoo").(int)+1)
   505  	}
   506  
   507  	r.MigrateState = func(
   508  		v int,
   509  		s *terraform.InstanceState,
   510  		meta interface{}) (*terraform.InstanceState, error) {
   511  		// Real state migration functions will probably switch on this value,
   512  		// but we'll just assert on it for now.
   513  		if v != 1 {
   514  			t.Fatalf("Expected StateSchemaVersion to be 1, got %d", v)
   515  		}
   516  
   517  		if meta != 42 {
   518  			t.Fatal("Expected meta to be passed through to the migration function")
   519  		}
   520  
   521  		oldfoo, err := strconv.ParseFloat(s.Attributes["oldfoo"], 64)
   522  		if err != nil {
   523  			t.Fatalf("err: %#v", err)
   524  		}
   525  		s.Attributes["newfoo"] = strconv.Itoa((int(oldfoo * 10)))
   526  		delete(s.Attributes, "oldfoo")
   527  
   528  		return s, nil
   529  	}
   530  
   531  	// State is v1 and deals in oldfoo, which tracked foo as a float at 1/10th
   532  	// the scale of newfoo
   533  	s := &terraform.InstanceState{
   534  		ID: "bar",
   535  		Attributes: map[string]string{
   536  			"oldfoo": "1.2",
   537  		},
   538  		Meta: map[string]string{
   539  			"schema_version": "1",
   540  		},
   541  	}
   542  
   543  	actual, err := r.Refresh(s, 42)
   544  	if err != nil {
   545  		t.Fatalf("err: %s", err)
   546  	}
   547  
   548  	expected := &terraform.InstanceState{
   549  		ID: "bar",
   550  		Attributes: map[string]string{
   551  			"id":     "bar",
   552  			"newfoo": "13",
   553  		},
   554  		Meta: map[string]string{
   555  			"schema_version": "2",
   556  		},
   557  	}
   558  
   559  	if !reflect.DeepEqual(actual, expected) {
   560  		t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual)
   561  	}
   562  }
   563  
   564  func TestResourceRefresh_noMigrationNeeded(t *testing.T) {
   565  	r := &Resource{
   566  		SchemaVersion: 2,
   567  		Schema: map[string]*Schema{
   568  			"newfoo": &Schema{
   569  				Type:     TypeInt,
   570  				Optional: true,
   571  			},
   572  		},
   573  	}
   574  
   575  	r.Read = func(d *ResourceData, m interface{}) error {
   576  		return d.Set("newfoo", d.Get("newfoo").(int)+1)
   577  	}
   578  
   579  	r.MigrateState = func(
   580  		v int,
   581  		s *terraform.InstanceState,
   582  		meta interface{}) (*terraform.InstanceState, error) {
   583  		t.Fatal("Migrate function shouldn't be called!")
   584  		return nil, nil
   585  	}
   586  
   587  	s := &terraform.InstanceState{
   588  		ID: "bar",
   589  		Attributes: map[string]string{
   590  			"newfoo": "12",
   591  		},
   592  		Meta: map[string]string{
   593  			"schema_version": "2",
   594  		},
   595  	}
   596  
   597  	actual, err := r.Refresh(s, nil)
   598  	if err != nil {
   599  		t.Fatalf("err: %s", err)
   600  	}
   601  
   602  	expected := &terraform.InstanceState{
   603  		ID: "bar",
   604  		Attributes: map[string]string{
   605  			"id":     "bar",
   606  			"newfoo": "13",
   607  		},
   608  		Meta: map[string]string{
   609  			"schema_version": "2",
   610  		},
   611  	}
   612  
   613  	if !reflect.DeepEqual(actual, expected) {
   614  		t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual)
   615  	}
   616  }
   617  
   618  func TestResourceRefresh_stateSchemaVersionUnset(t *testing.T) {
   619  	r := &Resource{
   620  		// Version 1 > Version 0
   621  		SchemaVersion: 1,
   622  		Schema: map[string]*Schema{
   623  			"newfoo": &Schema{
   624  				Type:     TypeInt,
   625  				Optional: true,
   626  			},
   627  		},
   628  	}
   629  
   630  	r.Read = func(d *ResourceData, m interface{}) error {
   631  		return d.Set("newfoo", d.Get("newfoo").(int)+1)
   632  	}
   633  
   634  	r.MigrateState = func(
   635  		v int,
   636  		s *terraform.InstanceState,
   637  		meta interface{}) (*terraform.InstanceState, error) {
   638  		s.Attributes["newfoo"] = s.Attributes["oldfoo"]
   639  		return s, nil
   640  	}
   641  
   642  	s := &terraform.InstanceState{
   643  		ID: "bar",
   644  		Attributes: map[string]string{
   645  			"oldfoo": "12",
   646  		},
   647  	}
   648  
   649  	actual, err := r.Refresh(s, nil)
   650  	if err != nil {
   651  		t.Fatalf("err: %s", err)
   652  	}
   653  
   654  	expected := &terraform.InstanceState{
   655  		ID: "bar",
   656  		Attributes: map[string]string{
   657  			"id":     "bar",
   658  			"newfoo": "13",
   659  		},
   660  		Meta: map[string]string{
   661  			"schema_version": "1",
   662  		},
   663  	}
   664  
   665  	if !reflect.DeepEqual(actual, expected) {
   666  		t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual)
   667  	}
   668  }
   669  
   670  func TestResourceRefresh_migrateStateErr(t *testing.T) {
   671  	r := &Resource{
   672  		SchemaVersion: 2,
   673  		Schema: map[string]*Schema{
   674  			"newfoo": &Schema{
   675  				Type:     TypeInt,
   676  				Optional: true,
   677  			},
   678  		},
   679  	}
   680  
   681  	r.Read = func(d *ResourceData, m interface{}) error {
   682  		t.Fatal("Read should never be called!")
   683  		return nil
   684  	}
   685  
   686  	r.MigrateState = func(
   687  		v int,
   688  		s *terraform.InstanceState,
   689  		meta interface{}) (*terraform.InstanceState, error) {
   690  		return s, fmt.Errorf("triggering an error")
   691  	}
   692  
   693  	s := &terraform.InstanceState{
   694  		ID: "bar",
   695  		Attributes: map[string]string{
   696  			"oldfoo": "12",
   697  		},
   698  	}
   699  
   700  	_, err := r.Refresh(s, nil)
   701  	if err == nil {
   702  		t.Fatal("expected error, but got none!")
   703  	}
   704  }