mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-15 19:22:46 -06:00
d749420a25
A fresh checkout of `origin/master` does not build atm using the `dev` target because `master` has not been formatted using `gofmt` from Go 1.11 (tis has been the case for a while if you've been running devel). None of the drift in question is especially new but now that Go 1.11 has been released and gofmt's formatting guidelines have been updated, it would be *really* nice if the code in `master` reflected the current tooling in order to avoid having to fight this drift locally. * 8mo: https://github.com/hashicorp/terraform/blame/master/backend/remote-state/s3/backend_test.go#L260-L261 * 6mo: https://github.com/hashicorp/terraform/blame/master/builtin/provisioners/chef/linux_provisioner_test.go#L124 * 1yr:7cfeffe36b/command/init.go (L75-L76)
* 12d:7cfeffe36b/command/meta_backend_test.go (L1437)
* 2yr:7cfeffe36b/helper/schema/resource_timeout_test.go (L26)
* 4yr:7cfeffe36b/helper/schema/schema_test.go (L2059)
* 1yr:7cfeffe36b/plugin/discovery/get_test.go (L151)
353 lines
9.4 KiB
Go
353 lines
9.4 KiB
Go
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
|
|
}{
|
|
{
|
|
Name: "Source does not define 'delete' key",
|
|
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0),
|
|
Config: expectedConfigForValues(2, 0, 0, 1, 0),
|
|
Expected: timeoutForValues(10, 0, 5, 0, 0),
|
|
ShouldErr: true,
|
|
},
|
|
{
|
|
Name: "Config overrides create",
|
|
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0),
|
|
Config: expectedConfigForValues(2, 0, 7, 0, 0),
|
|
Expected: timeoutForValues(2, 0, 7, 0, 0),
|
|
ShouldErr: false,
|
|
},
|
|
{
|
|
Name: "Config overrides create, default provided. Should still have zero values",
|
|
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3),
|
|
Config: expectedConfigForValues(2, 0, 7, 0, 0),
|
|
Expected: timeoutForValues(2, 0, 7, 0, 3),
|
|
ShouldErr: false,
|
|
},
|
|
{
|
|
Name: "Use something besides 'minutes'",
|
|
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{}{
|
|
"foo": "bar",
|
|
TimeoutsConfigKey: c.Config,
|
|
})
|
|
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",
|
|
TimeoutsConfigKey: []map[string]interface{}{
|
|
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
|
|
}
|