2017-03-02 18:07:49 +01:00
|
|
|
package schema
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/config"
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestResourceTimeout_ConfigDecode_badkey(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Name string
|
|
|
|
// what the resource has defined in source
|
|
|
|
ResourceDefaultTimeout *ResourceTimeout
|
|
|
|
// configuration provider by user in tf file
|
|
|
|
Config []map[string]interface{}
|
|
|
|
// what we expect the parsed ResourceTimeout to be
|
|
|
|
Expected *ResourceTimeout
|
|
|
|
// Should we have an error (key not defined in source)
|
|
|
|
ShouldErr bool
|
|
|
|
}{
|
|
|
|
{
|
2018-09-09 19:18:08 +02:00
|
|
|
Name: "Source does not define 'delete' key",
|
2017-03-02 18:07:49 +01:00
|
|
|
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0),
|
|
|
|
Config: expectedConfigForValues(2, 0, 0, 1, 0),
|
|
|
|
Expected: timeoutForValues(10, 0, 5, 0, 0),
|
|
|
|
ShouldErr: true,
|
|
|
|
},
|
|
|
|
{
|
2018-09-09 19:18:08 +02:00
|
|
|
Name: "Config overrides create",
|
2017-03-02 18:07:49 +01:00
|
|
|
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0),
|
|
|
|
Config: expectedConfigForValues(2, 0, 7, 0, 0),
|
|
|
|
Expected: timeoutForValues(2, 0, 7, 0, 0),
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
{
|
2018-09-09 19:18:08 +02:00
|
|
|
Name: "Config overrides create, default provided. Should still have zero values",
|
2017-03-02 18:07:49 +01:00
|
|
|
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3),
|
|
|
|
Config: expectedConfigForValues(2, 0, 7, 0, 0),
|
|
|
|
Expected: timeoutForValues(2, 0, 7, 0, 3),
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
{
|
2018-09-09 19:18:08 +02:00
|
|
|
Name: "Use something besides 'minutes'",
|
2017-03-02 18:07:49 +01:00
|
|
|
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3),
|
|
|
|
Config: []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"create": "2h",
|
|
|
|
}},
|
|
|
|
Expected: timeoutForValues(120, 0, 5, 0, 3),
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, c := range cases {
|
|
|
|
t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) {
|
|
|
|
r := &Resource{
|
|
|
|
Timeouts: c.ResourceDefaultTimeout,
|
|
|
|
}
|
|
|
|
|
|
|
|
raw, err := config.NewRawConfig(
|
|
|
|
map[string]interface{}{
|
2017-03-09 21:40:14 +01:00
|
|
|
"foo": "bar",
|
|
|
|
TimeoutsConfigKey: c.Config,
|
2017-03-02 18:07:49 +01:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
conf := terraform.NewResourceConfig(raw)
|
|
|
|
|
|
|
|
timeout := &ResourceTimeout{}
|
|
|
|
decodeErr := timeout.ConfigDecode(r, conf)
|
|
|
|
if c.ShouldErr {
|
|
|
|
if decodeErr == nil {
|
|
|
|
t.Fatalf("ConfigDecode case (%d): Expected bad timeout key: %s", i, decodeErr)
|
|
|
|
}
|
|
|
|
// should error, err was not nil, continue
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
if decodeErr != nil {
|
|
|
|
// should not error, error was not nil, fatal
|
|
|
|
t.Fatalf("decodeError was not nil: %s", decodeErr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(c.Expected, timeout) {
|
|
|
|
t.Fatalf("ConfigDecode match error case (%d), expected:\n%#v\ngot:\n%#v", i, c.Expected, timeout)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceTimeout_ConfigDecode(t *testing.T) {
|
|
|
|
r := &Resource{
|
|
|
|
Timeouts: &ResourceTimeout{
|
|
|
|
Create: DefaultTimeout(10 * time.Minute),
|
|
|
|
Update: DefaultTimeout(5 * time.Minute),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
raw, err := config.NewRawConfig(
|
|
|
|
map[string]interface{}{
|
|
|
|
"foo": "bar",
|
2017-03-09 21:40:14 +01:00
|
|
|
TimeoutsConfigKey: []map[string]interface{}{
|
2017-03-02 18:07:49 +01:00
|
|
|
map[string]interface{}{
|
|
|
|
"create": "2m",
|
|
|
|
},
|
|
|
|
map[string]interface{}{
|
|
|
|
"update": "1m",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
c := terraform.NewResourceConfig(raw)
|
|
|
|
|
|
|
|
timeout := &ResourceTimeout{}
|
|
|
|
err = timeout.ConfigDecode(r, c)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Expected good timeout returned:, %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := &ResourceTimeout{
|
|
|
|
Create: DefaultTimeout(2 * time.Minute),
|
|
|
|
Update: DefaultTimeout(1 * time.Minute),
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(timeout, expected) {
|
|
|
|
t.Fatalf("bad timeout decode, expected (%#v), got (%#v)", expected, timeout)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceTimeout_DiffEncode_basic(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Timeout *ResourceTimeout
|
|
|
|
Expected map[string]interface{}
|
|
|
|
// Not immediately clear when an error would hit
|
|
|
|
ShouldErr bool
|
|
|
|
}{
|
|
|
|
// Two fields
|
|
|
|
{
|
|
|
|
Timeout: timeoutForValues(10, 0, 5, 0, 0),
|
|
|
|
Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)},
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
// Two fields, one is Default
|
|
|
|
{
|
|
|
|
Timeout: timeoutForValues(10, 0, 0, 0, 7),
|
|
|
|
Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)},
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
// All fields
|
|
|
|
{
|
|
|
|
Timeout: timeoutForValues(10, 3, 4, 1, 7),
|
|
|
|
Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)},
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
// No fields
|
|
|
|
{
|
|
|
|
Timeout: &ResourceTimeout{},
|
|
|
|
Expected: nil,
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
state := &terraform.InstanceDiff{}
|
|
|
|
err := c.Timeout.DiffEncode(state)
|
|
|
|
if err != nil && !c.ShouldErr {
|
|
|
|
t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
// should maybe just compare [TimeoutKey] but for now we're assuming only
|
|
|
|
// that in Meta
|
|
|
|
if !reflect.DeepEqual(state.Meta, c.Expected) {
|
|
|
|
t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// same test cases but for InstanceState
|
|
|
|
for _, c := range cases {
|
|
|
|
state := &terraform.InstanceState{}
|
|
|
|
err := c.Timeout.StateEncode(state)
|
|
|
|
if err != nil && !c.ShouldErr {
|
|
|
|
t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
// should maybe just compare [TimeoutKey] but for now we're assuming only
|
|
|
|
// that in Meta
|
|
|
|
if !reflect.DeepEqual(state.Meta, c.Expected) {
|
|
|
|
t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceTimeout_MetaDecode_basic(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
State *terraform.InstanceDiff
|
|
|
|
Expected *ResourceTimeout
|
|
|
|
// Not immediately clear when an error would hit
|
|
|
|
ShouldErr bool
|
|
|
|
}{
|
|
|
|
// Two fields
|
|
|
|
{
|
|
|
|
State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}},
|
|
|
|
Expected: timeoutForValues(10, 0, 5, 0, 0),
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
// Two fields, one is Default
|
|
|
|
{
|
|
|
|
State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}},
|
|
|
|
Expected: timeoutForValues(10, 7, 7, 7, 7),
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
// All fields
|
|
|
|
{
|
|
|
|
State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}},
|
|
|
|
Expected: timeoutForValues(10, 3, 4, 1, 7),
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
// No fields
|
|
|
|
{
|
|
|
|
State: &terraform.InstanceDiff{},
|
|
|
|
Expected: &ResourceTimeout{},
|
|
|
|
ShouldErr: false,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
rt := &ResourceTimeout{}
|
|
|
|
err := rt.DiffDecode(c.State)
|
|
|
|
if err != nil && !c.ShouldErr {
|
|
|
|
t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, rt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// should maybe just compare [TimeoutKey] but for now we're assuming only
|
|
|
|
// that in Meta
|
|
|
|
if !reflect.DeepEqual(rt, c.Expected) {
|
|
|
|
t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, rt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func timeoutForValues(create, read, update, del, def int) *ResourceTimeout {
|
|
|
|
rt := ResourceTimeout{}
|
|
|
|
|
|
|
|
if create != 0 {
|
|
|
|
rt.Create = DefaultTimeout(time.Duration(create) * time.Minute)
|
|
|
|
}
|
|
|
|
if read != 0 {
|
|
|
|
rt.Read = DefaultTimeout(time.Duration(read) * time.Minute)
|
|
|
|
}
|
|
|
|
if update != 0 {
|
|
|
|
rt.Update = DefaultTimeout(time.Duration(update) * time.Minute)
|
|
|
|
}
|
|
|
|
if del != 0 {
|
|
|
|
rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute)
|
|
|
|
}
|
|
|
|
|
|
|
|
if def != 0 {
|
|
|
|
rt.Default = DefaultTimeout(time.Duration(def) * time.Minute)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &rt
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generates a ResourceTimeout struct that should reflect the
|
|
|
|
// d.Timeout("key") results
|
|
|
|
func expectedTimeoutForValues(create, read, update, del, def int) *ResourceTimeout {
|
|
|
|
rt := ResourceTimeout{}
|
|
|
|
|
|
|
|
defaultValues := []*int{&create, &read, &update, &del, &def}
|
|
|
|
for _, v := range defaultValues {
|
|
|
|
if *v == 0 {
|
|
|
|
*v = 20
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if create != 0 {
|
|
|
|
rt.Create = DefaultTimeout(time.Duration(create) * time.Minute)
|
|
|
|
}
|
|
|
|
if read != 0 {
|
|
|
|
rt.Read = DefaultTimeout(time.Duration(read) * time.Minute)
|
|
|
|
}
|
|
|
|
if update != 0 {
|
|
|
|
rt.Update = DefaultTimeout(time.Duration(update) * time.Minute)
|
|
|
|
}
|
|
|
|
if del != 0 {
|
|
|
|
rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute)
|
|
|
|
}
|
|
|
|
|
|
|
|
if def != 0 {
|
|
|
|
rt.Default = DefaultTimeout(time.Duration(def) * time.Minute)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &rt
|
|
|
|
}
|
|
|
|
|
|
|
|
func expectedForValues(create, read, update, del, def int) map[string]interface{} {
|
|
|
|
ex := make(map[string]interface{})
|
|
|
|
|
|
|
|
if create != 0 {
|
|
|
|
ex["create"] = DefaultTimeout(time.Duration(create) * time.Minute).Nanoseconds()
|
|
|
|
}
|
|
|
|
if read != 0 {
|
|
|
|
ex["read"] = DefaultTimeout(time.Duration(read) * time.Minute).Nanoseconds()
|
|
|
|
}
|
|
|
|
if update != 0 {
|
|
|
|
ex["update"] = DefaultTimeout(time.Duration(update) * time.Minute).Nanoseconds()
|
|
|
|
}
|
|
|
|
if del != 0 {
|
|
|
|
ex["delete"] = DefaultTimeout(time.Duration(del) * time.Minute).Nanoseconds()
|
|
|
|
}
|
|
|
|
|
|
|
|
if def != 0 {
|
|
|
|
defNano := DefaultTimeout(time.Duration(def) * time.Minute).Nanoseconds()
|
|
|
|
ex["default"] = defNano
|
|
|
|
|
|
|
|
for _, k := range timeoutKeys() {
|
|
|
|
if _, ok := ex[k]; !ok {
|
|
|
|
ex[k] = defNano
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ex
|
|
|
|
}
|
|
|
|
|
|
|
|
func expectedConfigForValues(create, read, update, delete, def int) []map[string]interface{} {
|
|
|
|
ex := make([]map[string]interface{}, 0)
|
|
|
|
|
|
|
|
if create != 0 {
|
|
|
|
ex = append(ex, map[string]interface{}{"create": fmt.Sprintf("%dm", create)})
|
|
|
|
}
|
|
|
|
if read != 0 {
|
|
|
|
ex = append(ex, map[string]interface{}{"read": fmt.Sprintf("%dm", read)})
|
|
|
|
}
|
|
|
|
if update != 0 {
|
|
|
|
ex = append(ex, map[string]interface{}{"update": fmt.Sprintf("%dm", update)})
|
|
|
|
}
|
|
|
|
if delete != 0 {
|
|
|
|
ex = append(ex, map[string]interface{}{"delete": fmt.Sprintf("%dm", delete)})
|
|
|
|
}
|
|
|
|
|
|
|
|
if def != 0 {
|
|
|
|
ex = append(ex, map[string]interface{}{"default": fmt.Sprintf("%dm", def)})
|
|
|
|
}
|
|
|
|
return ex
|
|
|
|
}
|