Skip to content

Commit 61c354d

Browse files
committed
Refactoring to use a supplied default timeout if the requested timeout is not in the Terraform configuration (#17)
1 parent ea45484 commit 61c354d

File tree

5 files changed

+41
-95
lines changed

5 files changed

+41
-95
lines changed

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Terraform configuration.
3838

3939
The following illustrates nested block syntax for defining timeouts on a resource and a data source.
4040

41-
```hcl
41+
```terraform
4242
resource "timeouts_example" "example" {
4343
/* ... */
4444
@@ -48,7 +48,7 @@ resource "timeouts_example" "example" {
4848
}
4949
```
5050

51-
```hcl
51+
```terraform
5252
data "timeouts_example" "example" {
5353
/* ... */
5454
@@ -104,7 +104,7 @@ func (t exampleDataSource) Schema(ctx context.Context, req datasource.SchemaRequ
104104
105105
The following illustrates nested attribute syntax for defining timeouts on a resource and a data source.
106106
107-
```hcl
107+
```terraform
108108
resource "timeouts_example" "example" {
109109
/* ... */
110110

@@ -114,7 +114,7 @@ resource "timeouts_example" "example" {
114114
}
115115
```
116116
117-
```hcl
117+
```terraform
118118
data "timeouts_example" "example" {
119119
/* ... */
120120

@@ -197,8 +197,10 @@ func (r exampleResource) Create(ctx context.Context, req resource.CreateRequest,
197197
if resp.Diagnostics.HasError() {
198198
return
199199
}
200-
201-
createTimeout, err := data.Timeouts.Create(ctx)
200+
201+
// Create() is passed a default timeout to use if no value
202+
// has been supplied in the Terraform configuration.
203+
createTimeout, err := data.Timeouts.Create(ctx, 20*time.Minute)
202204
if err != nil {
203205
// handle error
204206
}

datasource/timeouts/timeouts.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ import (
99
"github.com/hashicorp/terraform-plugin-framework/diag"
1010
"github.com/hashicorp/terraform-plugin-framework/types"
1111
"github.com/hashicorp/terraform-plugin-go/tftypes"
12+
"github.com/hashicorp/terraform-plugin-log/tflog"
1213
)
1314

14-
const defaultTimeout = time.Minute * 20
15-
1615
// Type is an attribute type that represents timeouts.
1716
type Type struct {
1817
types.ObjectType
@@ -65,7 +64,7 @@ func (t Value) Equal(c attr.Value) bool {
6564
return t.Object.Equal(other.Object)
6665
}
6766

68-
// Type returns an Type with the same attribute types as `t`.
67+
// Type returns a Type with the same attribute types as `t`.
6968
func (t Value) Type(ctx context.Context) attr.Type {
7069
return Type{
7170
types.ObjectType{
@@ -75,20 +74,17 @@ func (t Value) Type(ctx context.Context) attr.Type {
7574
}
7675

7776
// Read attempts to retrieve the "read" attribute and parse it as time.Duration.
78-
// If any diagnostics are generated they are returned along with the default timeout of 20 minutes.
79-
func (t Value) Read(ctx context.Context) (time.Duration, diag.Diagnostics) {
80-
return t.getTimeout(ctx, attributeNameRead)
77+
// If any diagnostics are generated they are returned along with the supplied default timeout.
78+
func (t Value) Read(ctx context.Context, defaultTimeout time.Duration) (time.Duration, diag.Diagnostics) {
79+
return t.getTimeout(ctx, attributeNameRead, defaultTimeout)
8180
}
8281

83-
func (t Value) getTimeout(_ context.Context, timeoutName string) (time.Duration, diag.Diagnostics) {
82+
func (t Value) getTimeout(ctx context.Context, timeoutName string, defaultTimeout time.Duration) (time.Duration, diag.Diagnostics) {
8483
var diags diag.Diagnostics
8584

8685
value, ok := t.Object.Attributes()[timeoutName]
8786
if !ok {
88-
diags.Append(diag.NewErrorDiagnostic(
89-
"Timeout Does Not Exist",
90-
fmt.Sprintf("timeout for %q does not exist", timeoutName),
91-
))
87+
tflog.Info(ctx, timeoutName+" timeout configuration not found, using provided default")
9288

9389
return defaultTimeout, diags
9490
}

datasource/timeouts/timeouts_test.go

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,6 @@ func TestTimeoutsValueRead(t *testing.T) {
164164
Object: types.Object{},
165165
},
166166
expectedTimeout: 20 * time.Minute,
167-
expectedDiags: diag.Diagnostics{
168-
diag.NewErrorDiagnostic(
169-
"Timeout Does Not Exist",
170-
`timeout for "read" does not exist`,
171-
),
172-
},
173167
},
174168
"read-not-parseable-as-time-duration": {
175169
timeoutsValue: timeouts.Value{
@@ -197,24 +191,15 @@ func TestTimeoutsValueRead(t *testing.T) {
197191
t.Run(name, func(t *testing.T) {
198192
t.Parallel()
199193

200-
gotTimeout, gotErr := test.timeoutsValue.Read(context.Background())
194+
gotTimeout, gotErr := test.timeoutsValue.Read(context.Background(), 20*time.Minute)
201195

202196
if diff := cmp.Diff(gotTimeout, test.expectedTimeout); diff != "" {
203197
t.Errorf("unexpected timeout difference: %s", diff)
204198
}
205199

206-
if diff := cmp.Diff(gotErr, test.expectedDiags, equateErrorMessage); diff != "" {
200+
if diff := cmp.Diff(gotErr, test.expectedDiags); diff != "" {
207201
t.Errorf("unexpected err difference: %s", diff)
208202
}
209203
})
210204
}
211205
}
212-
213-
// equateErrorMessage reports errors to be equal if both are nil
214-
// or both have the same message.
215-
var equateErrorMessage = cmp.Comparer(func(x, y error) bool {
216-
if x == nil || y == nil {
217-
return x == nil && y == nil
218-
}
219-
return x.Error() == y.Error()
220-
})

resource/timeouts/timeouts.go

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ import (
99
"github.com/hashicorp/terraform-plugin-framework/diag"
1010
"github.com/hashicorp/terraform-plugin-framework/types"
1111
"github.com/hashicorp/terraform-plugin-go/tftypes"
12+
"github.com/hashicorp/terraform-plugin-log/tflog"
1213
)
1314

14-
const defaultTimeout = time.Minute * 20
15-
1615
// Type is an attribute type that represents timeouts.
1716
type Type struct {
1817
types.ObjectType
@@ -65,7 +64,7 @@ func (t Value) Equal(c attr.Value) bool {
6564
return t.Object.Equal(other.Object)
6665
}
6766

68-
// Type returns an Type with the same attribute types as `t`.
67+
// Type returns a Type with the same attribute types as `t`.
6968
func (t Value) Type(ctx context.Context) attr.Type {
7069
return Type{
7170
types.ObjectType{
@@ -75,38 +74,35 @@ func (t Value) Type(ctx context.Context) attr.Type {
7574
}
7675

7776
// Create attempts to retrieve the "create" attribute and parse it as time.Duration.
78-
// If any diagnostics are generated they are returned along with the default timeout of 20 minutes.
79-
func (t Value) Create(ctx context.Context) (time.Duration, diag.Diagnostics) {
80-
return t.getTimeout(ctx, attributeNameCreate)
77+
// If any diagnostics are generated they are returned along with the supplied default timeout.
78+
func (t Value) Create(ctx context.Context, defaultTimeout time.Duration) (time.Duration, diag.Diagnostics) {
79+
return t.getTimeout(ctx, attributeNameCreate, defaultTimeout)
8180
}
8281

8382
// Read attempts to retrieve the "read" attribute and parse it as time.Duration.
84-
// If any diagnostics are generated they are returned along with the default timeout of 20 minutes.
85-
func (t Value) Read(ctx context.Context) (time.Duration, diag.Diagnostics) {
86-
return t.getTimeout(ctx, attributeNameRead)
83+
// If any diagnostics are generated they are returned along with the supplied default timeout.
84+
func (t Value) Read(ctx context.Context, defaultTimeout time.Duration) (time.Duration, diag.Diagnostics) {
85+
return t.getTimeout(ctx, attributeNameRead, defaultTimeout)
8786
}
8887

8988
// Update attempts to retrieve the "update" attribute and parse it as time.Duration.
90-
// If any diagnostics are generated they are returned along with the default timeout of 20 minutes.
91-
func (t Value) Update(ctx context.Context) (time.Duration, diag.Diagnostics) {
92-
return t.getTimeout(ctx, attributeNameUpdate)
89+
// If any diagnostics are generated they are returned along with the supplied default timeout.
90+
func (t Value) Update(ctx context.Context, defaultTimeout time.Duration) (time.Duration, diag.Diagnostics) {
91+
return t.getTimeout(ctx, attributeNameUpdate, defaultTimeout)
9392
}
9493

9594
// Delete attempts to retrieve the "delete" attribute and parse it as time.Duration.
96-
// If any diagnostics are generated they are returned along with the default timeout of 20 minutes.
97-
func (t Value) Delete(ctx context.Context) (time.Duration, diag.Diagnostics) {
98-
return t.getTimeout(ctx, attributeNameDelete)
95+
// If any diagnostics are generated they are returned along with the supplied default timeout.
96+
func (t Value) Delete(ctx context.Context, defaultTimeout time.Duration) (time.Duration, diag.Diagnostics) {
97+
return t.getTimeout(ctx, attributeNameDelete, defaultTimeout)
9998
}
10099

101-
func (t Value) getTimeout(_ context.Context, timeoutName string) (time.Duration, diag.Diagnostics) {
100+
func (t Value) getTimeout(ctx context.Context, timeoutName string, defaultTimeout time.Duration) (time.Duration, diag.Diagnostics) {
102101
var diags diag.Diagnostics
103102

104103
value, ok := t.Object.Attributes()[timeoutName]
105104
if !ok {
106-
diags.Append(diag.NewErrorDiagnostic(
107-
"Timeout Does Not Exist",
108-
fmt.Sprintf("timeout for %q does not exist", timeoutName),
109-
))
105+
tflog.Info(ctx, timeoutName+" timeout configuration not found, using provided default")
110106

111107
return defaultTimeout, diags
112108
}

resource/timeouts/timeouts_test.go

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,6 @@ func TestTimeoutsValueCreate(t *testing.T) {
179179
Object: types.Object{},
180180
},
181181
expectedTimeout: 20 * time.Minute,
182-
expectedDiags: diag.Diagnostics{
183-
diag.NewErrorDiagnostic(
184-
"Timeout Does Not Exist",
185-
`timeout for "create" does not exist`,
186-
),
187-
},
188182
},
189183
"create-not-parseable-as-time-duration": {
190184
timeoutsValue: timeouts.Value{
@@ -212,13 +206,13 @@ func TestTimeoutsValueCreate(t *testing.T) {
212206
t.Run(name, func(t *testing.T) {
213207
t.Parallel()
214208

215-
gotTimeout, gotErr := test.timeoutsValue.Create(context.Background())
209+
gotTimeout, gotErr := test.timeoutsValue.Create(context.Background(), 20*time.Minute)
216210

217211
if diff := cmp.Diff(gotTimeout, test.expectedTimeout); diff != "" {
218212
t.Errorf("unexpected timeout difference: %s", diff)
219213
}
220214

221-
if diff := cmp.Diff(gotErr, test.expectedDiags, equateErrorMessage); diff != "" {
215+
if diff := cmp.Diff(gotErr, test.expectedDiags); diff != "" {
222216
t.Errorf("unexpected err difference: %s", diff)
223217
}
224218
})
@@ -253,12 +247,6 @@ func TestTimeoutsValueRead(t *testing.T) {
253247
Object: types.Object{},
254248
},
255249
expectedTimeout: 20 * time.Minute,
256-
expectedDiags: diag.Diagnostics{
257-
diag.NewErrorDiagnostic(
258-
"Timeout Does Not Exist",
259-
`timeout for "read" does not exist`,
260-
),
261-
},
262250
},
263251
"read-not-parseable-as-time-duration": {
264252
timeoutsValue: timeouts.Value{
@@ -286,13 +274,13 @@ func TestTimeoutsValueRead(t *testing.T) {
286274
t.Run(name, func(t *testing.T) {
287275
t.Parallel()
288276

289-
gotTimeout, gotErr := test.timeoutsValue.Read(context.Background())
277+
gotTimeout, gotErr := test.timeoutsValue.Read(context.Background(), 20*time.Minute)
290278

291279
if diff := cmp.Diff(gotTimeout, test.expectedTimeout); diff != "" {
292280
t.Errorf("unexpected timeout difference: %s", diff)
293281
}
294282

295-
if diff := cmp.Diff(gotErr, test.expectedDiags, equateErrorMessage); diff != "" {
283+
if diff := cmp.Diff(gotErr, test.expectedDiags); diff != "" {
296284
t.Errorf("unexpected err difference: %s", diff)
297285
}
298286
})
@@ -327,12 +315,6 @@ func TestTimeoutsValueUpdate(t *testing.T) {
327315
Object: types.Object{},
328316
},
329317
expectedTimeout: 20 * time.Minute,
330-
expectedDiags: diag.Diagnostics{
331-
diag.NewErrorDiagnostic(
332-
"Timeout Does Not Exist",
333-
`timeout for "update" does not exist`,
334-
),
335-
},
336318
},
337319
"update-not-parseable-as-time-duration": {
338320
timeoutsValue: timeouts.Value{
@@ -360,13 +342,13 @@ func TestTimeoutsValueUpdate(t *testing.T) {
360342
t.Run(name, func(t *testing.T) {
361343
t.Parallel()
362344

363-
gotTimeout, gotErr := test.timeoutsValue.Update(context.Background())
345+
gotTimeout, gotErr := test.timeoutsValue.Update(context.Background(), 20*time.Minute)
364346

365347
if diff := cmp.Diff(gotTimeout, test.expectedTimeout); diff != "" {
366348
t.Errorf("unexpected timeout difference: %s", diff)
367349
}
368350

369-
if diff := cmp.Diff(gotErr, test.expectedDiags, equateErrorMessage); diff != "" {
351+
if diff := cmp.Diff(gotErr, test.expectedDiags); diff != "" {
370352
t.Errorf("unexpected err difference: %s", diff)
371353
}
372354
})
@@ -401,12 +383,6 @@ func TestTimeoutsValueDelete(t *testing.T) {
401383
Object: types.Object{},
402384
},
403385
expectedTimeout: 20 * time.Minute,
404-
expectedDiags: diag.Diagnostics{
405-
diag.NewErrorDiagnostic(
406-
"Timeout Does Not Exist",
407-
`timeout for "delete" does not exist`,
408-
),
409-
},
410386
},
411387
"delete-not-parseable-as-time-duration": {
412388
timeoutsValue: timeouts.Value{
@@ -434,24 +410,15 @@ func TestTimeoutsValueDelete(t *testing.T) {
434410
t.Run(name, func(t *testing.T) {
435411
t.Parallel()
436412

437-
gotTimeout, gotErr := test.timeoutsValue.Delete(context.Background())
413+
gotTimeout, gotErr := test.timeoutsValue.Delete(context.Background(), 20*time.Minute)
438414

439415
if diff := cmp.Diff(gotTimeout, test.expectedTimeout); diff != "" {
440416
t.Errorf("unexpected timeout difference: %s", diff)
441417
}
442418

443-
if diff := cmp.Diff(gotErr, test.expectedDiags, equateErrorMessage); diff != "" {
419+
if diff := cmp.Diff(gotErr, test.expectedDiags); diff != "" {
444420
t.Errorf("unexpected err difference: %s", diff)
445421
}
446422
})
447423
}
448424
}
449-
450-
// equateErrorMessage reports errors to be equal if both are nil
451-
// or both have the same message.
452-
var equateErrorMessage = cmp.Comparer(func(x, y error) bool {
453-
if x == nil || y == nil {
454-
return x == nil && y == nil
455-
}
456-
return x.Error() == y.Error()
457-
})

0 commit comments

Comments
 (0)