Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions docs/actions/sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "time_sleep Action - terraform-provider-time"
subcategory: ""
description: |-
Waits for the specified duration to elapse before completing. This action is useful for introducing delays in Terraform workflows.
---

# time_sleep (Action)

Waits for the specified duration to elapse before completing. This action is useful for introducing delays in Terraform workflows.

## Example Usage

```terraform
resource "terraform_data" "trigger" {
lifecycle {
action_trigger {
events = [
after_create
]
actions = [
action.time_sleep.sleep
]
}
}
}

action "time_sleep" "sleep" {
config {
duration = "10s"
}
}
```

<!-- action schema generated by tfplugindocs -->
## Schema

### Required

- `duration` (String) [Time duration](https://golang.org/pkg/time/#ParseDuration) to wait. Must be a number immediately followed by a unit: `ms` (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). For example, `"30s"` for 30 seconds or `"1.5m"` for 90 seconds.
15 changes: 15 additions & 0 deletions examples/actions/time_sleep/action.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

resource "terraform_data" "trigger" {
lifecycle {
action_trigger {
events = [after_create]
actions = [action.time_sleep.sleep]
}
}
}

action "time_sleep" "sleep" {
config {
duration = "10s"
}
}
87 changes: 87 additions & 0 deletions internal/provider/action_time_sleep.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright IBM Corp. 2020, 2026
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"fmt"
"regexp"
"time"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/action"
"github.com/hashicorp/terraform-plugin-framework/action/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

// Ensure provider defined types fully satisfy framework interfaces.
var _ action.Action = &TimeSleepAction{}

func NewTimeSleepAction() action.Action {
return &TimeSleepAction{}
}

// TimeSleepAction defines the action implementation.
type TimeSleepAction struct{}

// TimeSleepActionModel describes the action data model.
type TimeSleepActionModel struct {
Duration types.String `tfsdk:"duration"`
}

func (a *TimeSleepAction) Metadata(ctx context.Context, req action.MetadataRequest, resp *action.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_sleep"
}

func (a *TimeSleepAction) Schema(ctx context.Context, req action.SchemaRequest, resp *action.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Waits for the specified duration to elapse before completing.",
MarkdownDescription: "Waits for the specified duration to elapse before completing. This action is useful for introducing delays in Terraform workflows.",
Attributes: map[string]schema.Attribute{
"duration": schema.StringAttribute{
Description: "Time duration to wait. Must be a valid Go duration string (e.g., \"30s\", \"5m\", \"1h\").",
MarkdownDescription: "[Time duration](https://golang.org/pkg/time/#ParseDuration) to wait. Must be a number immediately followed by a unit: `ms`" +
" (milliseconds), `s` (seconds), `m` (minutes), or `h` (hours). For example, `\"30s\"` for 30 seconds or `\"1.5m\"` for 90 seconds.",
Required: true,
Validators: []validator.String{
stringvalidator.RegexMatches(
regexp.MustCompile(`^[0-9]+(\.[0-9]+)?(ms|s|m|h)$`),
"must be a number immediately followed by ms (milliseconds), s (seconds), m (minutes), or h (hours). For example, \"30s\" for 30 seconds.",
),
},
},
},
}
}

func (a *TimeSleepAction) Invoke(ctx context.Context, req action.InvokeRequest, resp *action.InvokeResponse) {
var data TimeSleepActionModel

diags := req.Config.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

duration, err := time.ParseDuration(data.Duration.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Invalid Duration",
fmt.Sprintf("The duration value could not be parsed: %s\n\nOriginal Error: %s", data.Duration.ValueString(), err),
)
return
}

select {
case <-ctx.Done():
resp.Diagnostics.AddError(
"Action Cancelled",
fmt.Sprintf("The sleep action was cancelled: %s", ctx.Err()),
)
return
case <-time.After(duration):
// Sleep completed successfully
}
}
142 changes: 142 additions & 0 deletions internal/provider/action_time_sleep_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright IBM Corp. 2020, 2026
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"fmt"
"regexp"
"testing"
"time"

"github.com/hashicorp/terraform-plugin-framework/action"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

// Unit test to verify that sleeping works as expected for actions
func TestActionTimeSleepInvoke(t *testing.T) {
durationStr := "1s"
expectedDuration, err := time.ParseDuration("1s")

if err != nil {
t.Fatalf("unable to parse test duration: %s", err)
}

sleepAction := NewTimeSleepAction()

m := map[string]tftypes.Value{
"duration": tftypes.NewValue(tftypes.String, durationStr),
}
config := tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"duration": tftypes.String,
},
}, m)

schemaResponse := action.SchemaResponse{}
sleepAction.Schema(context.Background(), action.SchemaRequest{}, &schemaResponse)

req := action.InvokeRequest{
Config: tfsdk.Config{
Raw: config,
Schema: schemaResponse.Schema,
},
}

resp := action.InvokeResponse{
Diagnostics: nil,
}

start := time.Now()
sleepAction.Invoke(context.Background(), req, &resp)
end := time.Now()
elapsed := end.Sub(start)

if resp.Diagnostics.HasError() {
t.Fatalf("unexpected error during invoke: %v", resp.Diagnostics)
}

if elapsed < expectedDuration {
t.Errorf("did not sleep long enough, expected duration: %d got: %d", expectedDuration, elapsed)
}
}

func TestAccTimeSleepAction_Basic(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
ProtoV5ProviderFactories: protoV5ProviderFactories(),
Steps: []resource.TestStep{
{
Config: testAccConfigTimeSleepAction("1ms"),
},
{
Config: testAccConfigTimeSleepAction("2ms"),
},
},
})
}

func TestAccTimeSleepAction_Validators(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
ProtoV5ProviderFactories: protoV5ProviderFactories(),
Steps: []resource.TestStep{
{
Config: testAccConfigTimeSleepAction("1"),
ExpectError: regexp.MustCompile(`.*Error: Invalid Attribute Value Match`),
},
{
Config: testAccConfigTimeSleepAction("invalid"),
ExpectError: regexp.MustCompile(`.*Error: Invalid Attribute Value Match`),
},
{
Config: testAccConfigTimeSleepAction("30"),
ExpectError: regexp.MustCompile(`.*Error: Invalid Attribute Value Match`),
},
},
})
}

func TestAccTimeSleepAction_ValidDurations(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
ProtoV5ProviderFactories: protoV5ProviderFactories(),
Steps: []resource.TestStep{
{
Config: testAccConfigTimeSleepAction("100ms"),
},
{
Config: testAccConfigTimeSleepAction("1s"),
},
{
Config: testAccConfigTimeSleepAction("1m"),
},
{
Config: testAccConfigTimeSleepAction("1h"),
},
{
Config: testAccConfigTimeSleepAction("1.5s"),
},
},
})
}

func testAccConfigTimeSleepAction(duration string) string {
return fmt.Sprintf(`
terraform {
required_providers {
time = {
source = "hashicorp/time"
}
}
}

provider "time" {}

action "time_sleep" "test" {
config {
duration = %[1]q
}
}
`, duration)
}
7 changes: 7 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package provider
import (
"context"

"github.com/hashicorp/terraform-plugin-framework/action"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/function"
"github.com/hashicorp/terraform-plugin-framework/provider"
Expand Down Expand Up @@ -65,3 +66,9 @@ func (p *timeProvider) Functions(ctx context.Context) []func() function.Function
NewUnixTimestampParseFunction,
}
}

func (p *timeProvider) Actions(ctx context.Context) []func() action.Action {
return []func() action.Action{
NewTimeSleepAction,
}
}