diff --git a/pkg/config/resource.go b/pkg/config/resource.go index 5671ebda..33ce7328 100644 --- a/pkg/config/resource.go +++ b/pkg/config/resource.go @@ -18,6 +18,7 @@ package config import ( "context" + "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/pkg/errors" @@ -184,6 +185,16 @@ func (s *Sensitive) AddFieldPath(tf, xp string) { s.fieldPaths[tf] = xp } +// OperationTimeouts allows configuring resource operation timeouts: +// https://www.terraform.io/language/resources/syntax#operation-timeouts +// Please note that, not all resources support configuring timeouts. +type OperationTimeouts struct { + Read time.Duration + Create time.Duration + Update time.Duration + Delete time.Duration +} + // Resource is the set of information that you can override at different steps // of the code generation pipeline. type Resource struct { @@ -212,6 +223,9 @@ type Resource struct { // databases. UseAsync bool + // OperationTimeouts allows configuring resource operation timeouts. + OperationTimeouts OperationTimeouts + // ExternalName allows you to specify a custom ExternalName. ExternalName ExternalName diff --git a/pkg/terraform/files.go b/pkg/terraform/files.go index eddb6fec..b2e60969 100644 --- a/pkg/terraform/files.go +++ b/pkg/terraform/files.go @@ -150,6 +150,25 @@ func (fp *FileProducer) WriteMainTF() error { fp.parameters["lifecycle"] = map[string]bool{ "prevent_destroy": !meta.WasDeleted(fp.Resource), } + + // Add operation timeouts if any timeout configured for the resource + timeouts := map[string]string{} + if t := fp.Config.OperationTimeouts.Read.String(); t != "0s" { + timeouts["read"] = t + } + if t := fp.Config.OperationTimeouts.Create.String(); t != "0s" { + timeouts["create"] = t + } + if t := fp.Config.OperationTimeouts.Update.String(); t != "0s" { + timeouts["update"] = t + } + if t := fp.Config.OperationTimeouts.Delete.String(); t != "0s" { + timeouts["delete"] = t + } + if len(timeouts) != 0 { + fp.parameters["timeouts"] = timeouts + } + // Note(turkenh): To use third party providers, we need to configure // provider name in required_providers. providerSource := strings.Split(fp.Setup.Requirement.Source, "/") diff --git a/pkg/terraform/files_test.go b/pkg/terraform/files_test.go index b8ddae78..9cf823f6 100644 --- a/pkg/terraform/files_test.go +++ b/pkg/terraform/files_test.go @@ -20,6 +20,7 @@ import ( "context" "path/filepath" "testing" + "time" "github.com/crossplane/crossplane-runtime/pkg/meta" xpfake "github.com/crossplane/crossplane-runtime/pkg/resource/fake" @@ -96,8 +97,9 @@ func TestWriteTFState(t *testing.T) { func TestWriteMainTF(t *testing.T) { type args struct { - tr resource.Terraformed - s Setup + tr resource.Terraformed + cfg *config.Resource + s Setup } type want struct { maintf string @@ -108,6 +110,44 @@ func TestWriteMainTF(t *testing.T) { args want }{ + "TimeoutsConfigured": { + reason: "Configured resources should be able to write everything it has into maintf file", + args: args{ + tr: &fake.Terraformed{ + Managed: xpfake.Managed{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + resource.AnnotationKeyPrivateRawAttribute: "privateraw", + meta.AnnotationKeyExternalName: "some-id", + }, + }, + }, + Parameterizable: fake.Parameterizable{Parameters: map[string]interface{}{ + "param": "paramval", + }}, + Observable: fake.Observable{Observation: map[string]interface{}{ + "obs": "obsval", + }}, + }, + cfg: config.DefaultResource("terrajet_resource", nil, func(r *config.Resource) { + r.OperationTimeouts = config.OperationTimeouts{ + Read: 30 * time.Second, + Update: 2 * time.Minute, + } + }), + s: Setup{ + Requirement: ProviderRequirement{ + Source: "hashicorp/provider-test", + Version: "1.2.3", + }, + Configuration: nil, + Env: nil, + }, + }, + want: want{ + maintf: `{"provider":{"provider-test":null},"resource":{"":{"":{"lifecycle":{"prevent_destroy":true},"name":"some-id","param":"paramval","timeouts":{"read":"30s","update":"2m0s"}}}},"terraform":{"required_providers":{"provider-test":{"source":"hashicorp/provider-test","version":"1.2.3"}}}}`, + }, + }, "Success": { reason: "Standard resources should be able to write everything it has into maintf file", args: args{ @@ -127,6 +167,7 @@ func TestWriteMainTF(t *testing.T) { "obs": "obsval", }}, }, + cfg: config.DefaultResource("terrajet_resource", nil), s: Setup{ Requirement: ProviderRequirement{ Source: "hashicorp/provider-test", @@ -144,7 +185,7 @@ func TestWriteMainTF(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { fs := afero.NewMemMapFs() - fp, err := NewFileProducer(context.TODO(), nil, dir, tc.args.tr, tc.args.s, config.DefaultResource("terrajet_resource", nil), WithFileSystem(fs)) + fp, err := NewFileProducer(context.TODO(), nil, dir, tc.args.tr, tc.args.s, tc.args.cfg, WithFileSystem(fs)) if err != nil { t.Errorf("cannot initialize a file producer: %s", err.Error()) }