github.com/kcburge/terraform@v0.11.12-beta1/helper/schema/resource_timeout_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/config"
    10  	"github.com/hashicorp/terraform/terraform"
    11  )
    12  
    13  func TestResourceTimeout_ConfigDecode_badkey(t *testing.T) {
    14  	cases := []struct {
    15  		Name string
    16  		// what the resource has defined in source
    17  		ResourceDefaultTimeout *ResourceTimeout
    18  		// configuration provider by user in tf file
    19  		Config []map[string]interface{}
    20  		// what we expect the parsed ResourceTimeout to be
    21  		Expected *ResourceTimeout
    22  		// Should we have an error (key not defined in source)
    23  		ShouldErr bool
    24  	}{
    25  		{
    26  			Name:                   "Source does not define 'delete' key",
    27  			ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0),
    28  			Config:                 expectedConfigForValues(2, 0, 0, 1, 0),
    29  			Expected:               timeoutForValues(10, 0, 5, 0, 0),
    30  			ShouldErr:              true,
    31  		},
    32  		{
    33  			Name:                   "Config overrides create",
    34  			ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0),
    35  			Config:                 expectedConfigForValues(2, 0, 7, 0, 0),
    36  			Expected:               timeoutForValues(2, 0, 7, 0, 0),
    37  			ShouldErr:              false,
    38  		},
    39  		{
    40  			Name:                   "Config overrides create, default provided. Should still have zero values",
    41  			ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3),
    42  			Config:                 expectedConfigForValues(2, 0, 7, 0, 0),
    43  			Expected:               timeoutForValues(2, 0, 7, 0, 3),
    44  			ShouldErr:              false,
    45  		},
    46  		{
    47  			Name:                   "Use something besides 'minutes'",
    48  			ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3),
    49  			Config: []map[string]interface{}{
    50  				map[string]interface{}{
    51  					"create": "2h",
    52  				}},
    53  			Expected:  timeoutForValues(120, 0, 5, 0, 3),
    54  			ShouldErr: false,
    55  		},
    56  	}
    57  
    58  	for i, c := range cases {
    59  		t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) {
    60  			r := &Resource{
    61  				Timeouts: c.ResourceDefaultTimeout,
    62  			}
    63  
    64  			raw, err := config.NewRawConfig(
    65  				map[string]interface{}{
    66  					"foo":             "bar",
    67  					TimeoutsConfigKey: c.Config,
    68  				})
    69  			if err != nil {
    70  				t.Fatalf("err: %s", err)
    71  			}
    72  			conf := terraform.NewResourceConfig(raw)
    73  
    74  			timeout := &ResourceTimeout{}
    75  			decodeErr := timeout.ConfigDecode(r, conf)
    76  			if c.ShouldErr {
    77  				if decodeErr == nil {
    78  					t.Fatalf("ConfigDecode case (%d): Expected bad timeout key: %s", i, decodeErr)
    79  				}
    80  				// should error, err was not nil, continue
    81  				return
    82  			} else {
    83  				if decodeErr != nil {
    84  					// should not error, error was not nil, fatal
    85  					t.Fatalf("decodeError was not nil: %s", decodeErr)
    86  				}
    87  			}
    88  
    89  			if !reflect.DeepEqual(c.Expected, timeout) {
    90  				t.Fatalf("ConfigDecode match error case (%d), expected:\n%#v\ngot:\n%#v", i, c.Expected, timeout)
    91  			}
    92  		})
    93  	}
    94  }
    95  
    96  func TestResourceTimeout_ConfigDecode(t *testing.T) {
    97  	r := &Resource{
    98  		Timeouts: &ResourceTimeout{
    99  			Create: DefaultTimeout(10 * time.Minute),
   100  			Update: DefaultTimeout(5 * time.Minute),
   101  		},
   102  	}
   103  
   104  	raw, err := config.NewRawConfig(
   105  		map[string]interface{}{
   106  			"foo": "bar",
   107  			TimeoutsConfigKey: []map[string]interface{}{
   108  				map[string]interface{}{
   109  					"create": "2m",
   110  				},
   111  				map[string]interface{}{
   112  					"update": "1m",
   113  				},
   114  			},
   115  		})
   116  	if err != nil {
   117  		t.Fatalf("err: %s", err)
   118  	}
   119  	c := terraform.NewResourceConfig(raw)
   120  
   121  	timeout := &ResourceTimeout{}
   122  	err = timeout.ConfigDecode(r, c)
   123  	if err != nil {
   124  		t.Fatalf("Expected good timeout returned:, %s", err)
   125  	}
   126  
   127  	expected := &ResourceTimeout{
   128  		Create: DefaultTimeout(2 * time.Minute),
   129  		Update: DefaultTimeout(1 * time.Minute),
   130  	}
   131  
   132  	if !reflect.DeepEqual(timeout, expected) {
   133  		t.Fatalf("bad timeout decode, expected (%#v), got (%#v)", expected, timeout)
   134  	}
   135  }
   136  
   137  func TestResourceTimeout_DiffEncode_basic(t *testing.T) {
   138  	cases := []struct {
   139  		Timeout  *ResourceTimeout
   140  		Expected map[string]interface{}
   141  		// Not immediately clear when an error would hit
   142  		ShouldErr bool
   143  	}{
   144  		// Two fields
   145  		{
   146  			Timeout:   timeoutForValues(10, 0, 5, 0, 0),
   147  			Expected:  map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)},
   148  			ShouldErr: false,
   149  		},
   150  		// Two fields, one is Default
   151  		{
   152  			Timeout:   timeoutForValues(10, 0, 0, 0, 7),
   153  			Expected:  map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)},
   154  			ShouldErr: false,
   155  		},
   156  		// All fields
   157  		{
   158  			Timeout:   timeoutForValues(10, 3, 4, 1, 7),
   159  			Expected:  map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)},
   160  			ShouldErr: false,
   161  		},
   162  		// No fields
   163  		{
   164  			Timeout:   &ResourceTimeout{},
   165  			Expected:  nil,
   166  			ShouldErr: false,
   167  		},
   168  	}
   169  
   170  	for _, c := range cases {
   171  		state := &terraform.InstanceDiff{}
   172  		err := c.Timeout.DiffEncode(state)
   173  		if err != nil && !c.ShouldErr {
   174  			t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta)
   175  		}
   176  
   177  		// should maybe just compare [TimeoutKey] but for now we're assuming only
   178  		// that in Meta
   179  		if !reflect.DeepEqual(state.Meta, c.Expected) {
   180  			t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta)
   181  		}
   182  	}
   183  	// same test cases but for InstanceState
   184  	for _, c := range cases {
   185  		state := &terraform.InstanceState{}
   186  		err := c.Timeout.StateEncode(state)
   187  		if err != nil && !c.ShouldErr {
   188  			t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta)
   189  		}
   190  
   191  		// should maybe just compare [TimeoutKey] but for now we're assuming only
   192  		// that in Meta
   193  		if !reflect.DeepEqual(state.Meta, c.Expected) {
   194  			t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta)
   195  		}
   196  	}
   197  }
   198  
   199  func TestResourceTimeout_MetaDecode_basic(t *testing.T) {
   200  	cases := []struct {
   201  		State    *terraform.InstanceDiff
   202  		Expected *ResourceTimeout
   203  		// Not immediately clear when an error would hit
   204  		ShouldErr bool
   205  	}{
   206  		// Two fields
   207  		{
   208  			State:     &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}},
   209  			Expected:  timeoutForValues(10, 0, 5, 0, 0),
   210  			ShouldErr: false,
   211  		},
   212  		// Two fields, one is Default
   213  		{
   214  			State:     &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}},
   215  			Expected:  timeoutForValues(10, 7, 7, 7, 7),
   216  			ShouldErr: false,
   217  		},
   218  		// All fields
   219  		{
   220  			State:     &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}},
   221  			Expected:  timeoutForValues(10, 3, 4, 1, 7),
   222  			ShouldErr: false,
   223  		},
   224  		// No fields
   225  		{
   226  			State:     &terraform.InstanceDiff{},
   227  			Expected:  &ResourceTimeout{},
   228  			ShouldErr: false,
   229  		},
   230  	}
   231  
   232  	for _, c := range cases {
   233  		rt := &ResourceTimeout{}
   234  		err := rt.DiffDecode(c.State)
   235  		if err != nil && !c.ShouldErr {
   236  			t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, rt)
   237  		}
   238  
   239  		// should maybe just compare [TimeoutKey] but for now we're assuming only
   240  		// that in Meta
   241  		if !reflect.DeepEqual(rt, c.Expected) {
   242  			t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, rt)
   243  		}
   244  	}
   245  }
   246  
   247  func timeoutForValues(create, read, update, del, def int) *ResourceTimeout {
   248  	rt := ResourceTimeout{}
   249  
   250  	if create != 0 {
   251  		rt.Create = DefaultTimeout(time.Duration(create) * time.Minute)
   252  	}
   253  	if read != 0 {
   254  		rt.Read = DefaultTimeout(time.Duration(read) * time.Minute)
   255  	}
   256  	if update != 0 {
   257  		rt.Update = DefaultTimeout(time.Duration(update) * time.Minute)
   258  	}
   259  	if del != 0 {
   260  		rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute)
   261  	}
   262  
   263  	if def != 0 {
   264  		rt.Default = DefaultTimeout(time.Duration(def) * time.Minute)
   265  	}
   266  
   267  	return &rt
   268  }
   269  
   270  // Generates a ResourceTimeout struct that should reflect the
   271  // d.Timeout("key") results
   272  func expectedTimeoutForValues(create, read, update, del, def int) *ResourceTimeout {
   273  	rt := ResourceTimeout{}
   274  
   275  	defaultValues := []*int{&create, &read, &update, &del, &def}
   276  	for _, v := range defaultValues {
   277  		if *v == 0 {
   278  			*v = 20
   279  		}
   280  	}
   281  
   282  	if create != 0 {
   283  		rt.Create = DefaultTimeout(time.Duration(create) * time.Minute)
   284  	}
   285  	if read != 0 {
   286  		rt.Read = DefaultTimeout(time.Duration(read) * time.Minute)
   287  	}
   288  	if update != 0 {
   289  		rt.Update = DefaultTimeout(time.Duration(update) * time.Minute)
   290  	}
   291  	if del != 0 {
   292  		rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute)
   293  	}
   294  
   295  	if def != 0 {
   296  		rt.Default = DefaultTimeout(time.Duration(def) * time.Minute)
   297  	}
   298  
   299  	return &rt
   300  }
   301  
   302  func expectedForValues(create, read, update, del, def int) map[string]interface{} {
   303  	ex := make(map[string]interface{})
   304  
   305  	if create != 0 {
   306  		ex["create"] = DefaultTimeout(time.Duration(create) * time.Minute).Nanoseconds()
   307  	}
   308  	if read != 0 {
   309  		ex["read"] = DefaultTimeout(time.Duration(read) * time.Minute).Nanoseconds()
   310  	}
   311  	if update != 0 {
   312  		ex["update"] = DefaultTimeout(time.Duration(update) * time.Minute).Nanoseconds()
   313  	}
   314  	if del != 0 {
   315  		ex["delete"] = DefaultTimeout(time.Duration(del) * time.Minute).Nanoseconds()
   316  	}
   317  
   318  	if def != 0 {
   319  		defNano := DefaultTimeout(time.Duration(def) * time.Minute).Nanoseconds()
   320  		ex["default"] = defNano
   321  
   322  		for _, k := range timeoutKeys() {
   323  			if _, ok := ex[k]; !ok {
   324  				ex[k] = defNano
   325  			}
   326  		}
   327  	}
   328  
   329  	return ex
   330  }
   331  
   332  func expectedConfigForValues(create, read, update, delete, def int) []map[string]interface{} {
   333  	ex := make([]map[string]interface{}, 0)
   334  
   335  	if create != 0 {
   336  		ex = append(ex, map[string]interface{}{"create": fmt.Sprintf("%dm", create)})
   337  	}
   338  	if read != 0 {
   339  		ex = append(ex, map[string]interface{}{"read": fmt.Sprintf("%dm", read)})
   340  	}
   341  	if update != 0 {
   342  		ex = append(ex, map[string]interface{}{"update": fmt.Sprintf("%dm", update)})
   343  	}
   344  	if delete != 0 {
   345  		ex = append(ex, map[string]interface{}{"delete": fmt.Sprintf("%dm", delete)})
   346  	}
   347  
   348  	if def != 0 {
   349  		ex = append(ex, map[string]interface{}{"default": fmt.Sprintf("%dm", def)})
   350  	}
   351  	return ex
   352  }