Skip to content

Commit

Permalink
Merge pull request #4475 from terraform-providers/f/custom-timeouts
Browse files Browse the repository at this point in the history
Groundwork for Custom Timeouts
  • Loading branch information
tombuildsstuff authored Oct 4, 2019
2 parents 3a417d0 + 7fda615 commit d413ab8
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 5 deletions.
11 changes: 9 additions & 2 deletions azurerm/internal/common/client_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/Azure/go-autorest/autorest/azure"
"github.com/hashicorp/go-azure-helpers/sender"
"github.com/hashicorp/terraform/httpclient"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
"github.com/terraform-providers/terraform-provider-azurerm/version"
)

Expand All @@ -27,22 +28,28 @@ type ClientOptions struct {
ResourceManagerEndpoint string
StorageAuthorizer autorest.Authorizer

PollingDuration time.Duration
SkipProviderReg bool
DisableCorrelationRequestID bool
Environment azure.Environment

// TODO: remove me in 2.0
PollingDuration time.Duration
}

func (o ClientOptions) ConfigureClient(c *autorest.Client, authorizer autorest.Authorizer) {
setUserAgent(c, o.TerraformVersion, o.PartnerId)

c.Authorizer = authorizer
c.Sender = sender.BuildSender("AzureRM")
c.PollingDuration = o.PollingDuration
c.SkipResourceProviderRegistration = o.SkipProviderReg
if !o.DisableCorrelationRequestID {
c.RequestInspector = WithCorrelationRequestID(CorrelationRequestID())
}

// TODO: remove in 2.0
if !features.SupportsCustomTimeouts() {
c.PollingDuration = o.PollingDuration
}
}

func setUserAgent(client *autorest.Client, tfVersion, partnerID string) {
Expand Down
22 changes: 22 additions & 0 deletions azurerm/internal/features/custom_timeouts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package features

import (
"os"
"strings"
)

// SupportsCustomTimeouts returns whether Custom Timeouts are supported
//
// This feature allows Resources to define Custom Timeouts for Creation, Updating and Deletion
// which helps work with Azure resources that take longer to provision/delete.
// When this feature is disabled, all resources have a hard-coded timeout of 3 hours.
//
// This feature-toggle defaults to off in 1.x versions of the Azure Provider, however this will
// become the default behaviour in version 2.0 of the Azure Provider. As outlined in the announcement
// for v2.0 of the Azure Provider: https://github.com/terraform-providers/terraform-provider-azurerm/issues/2807
//
// Operators wishing to adopt this behaviour can opt-into this behaviour in 1.x versions of the
// Azure Provider by setting the Environment Variable 'ARM_PROVIDER_CUSTOM_TIMEOUTS' to 'true'
func SupportsCustomTimeouts() bool {
return strings.EqualFold(os.Getenv("ARM_PROVIDER_CUSTOM_TIMEOUTS"), "true")
}
55 changes: 55 additions & 0 deletions azurerm/internal/features/custom_timeouts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package features

import (
"os"
"testing"
)

func TestCustomTimeouts(t *testing.T) {
testData := []struct {
name string
value string
expected bool
}{
{
name: "unset",
value: "",
expected: false,
},
{
name: "disabled lower-case",
value: "false",
expected: false,
},
{
name: "disabled upper-case",
value: "FALSE",
expected: false,
},
{
name: "enabled lower-case",
value: "true",
expected: true,
},
{
name: "enabled upper-case",
value: "TRUE",
expected: true,
},
{
name: "invalid",
value: "pandas",
expected: false,
},
}

for _, v := range testData {
t.Logf("[DEBUG] Test %q..", v.name)

os.Setenv("ARM_PROVIDER_CUSTOM_TIMEOUTS", v.value)
actual := SupportsCustomTimeouts()
if v.expected != actual {
t.Fatalf("Expected %t but got %t", v.expected, actual)
}
}
}
66 changes: 66 additions & 0 deletions azurerm/internal/timeouts/determine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package timeouts

import (
"context"
"time"

"github.com/hashicorp/terraform/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
)

// TODO: tests for this

// ForCreate returns the context wrapped with the timeout for an Create operation
//
// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
// Otherwise this returns the default context
func ForCreate(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
return buildWithTimeout(ctx, d.Timeout(schema.TimeoutCreate))
}

// ForCreateUpdate returns the context wrapped with the timeout for an combined Create/Update operation
//
// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
// Otherwise this returns the default context
func ForCreateUpdate(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
if d.IsNewResource() {
return ForCreate(ctx, d)
}

return ForUpdate(ctx, d)
}

// ForDelete returns the context wrapped with the timeout for an Delete operation
//
// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
// Otherwise this returns the default context
func ForDelete(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
return buildWithTimeout(ctx, d.Timeout(schema.TimeoutDelete))
}

// ForRead returns the context wrapped with the timeout for an Read operation
//
// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
// Otherwise this returns the default context
func ForRead(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
return buildWithTimeout(ctx, d.Timeout(schema.TimeoutRead))
}

// ForUpdate returns the context wrapped with the timeout for an Update operation
//
// If the 'SupportsCustomTimeouts' feature toggle is enabled - this is wrapped with a context
// Otherwise this returns the default context
func ForUpdate(ctx context.Context, d *schema.ResourceData) (context.Context, context.CancelFunc) {
return buildWithTimeout(ctx, d.Timeout(schema.TimeoutUpdate))
}

func buildWithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) {
if features.SupportsCustomTimeouts() {
return context.WithTimeout(ctx, timeout)
}

nullFunc := func() {
// do nothing on cancel since timeouts aren't enabled
}
return ctx, nullFunc
}
25 changes: 25 additions & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"log"
"os"
"strings"
"time"

"github.com/hashicorp/go-azure-helpers/authentication"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/common"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
Expand Down Expand Up @@ -480,6 +482,29 @@ func Provider() terraform.ResourceProvider {
}
}

// TODO: remove all of this in 2.0 once Custom Timeouts are supported
if features.SupportsCustomTimeouts() {
// default everything to 3 hours for now
for _, v := range resources {
if v.Timeouts == nil {
v.Timeouts = &schema.ResourceTimeout{
Create: schema.DefaultTimeout(3 * time.Hour),
Update: schema.DefaultTimeout(3 * time.Hour),
Delete: schema.DefaultTimeout(3 * time.Hour),
Default: schema.DefaultTimeout(3 * time.Hour),

// Read is the only exception, since if it's taken more than 5 minutes something's seriously wrong
Read: schema.DefaultTimeout(5 * time.Minute),
}
}
}
} else {
// ensure any timeouts configured on the resources are removed until 2.0
for _, v := range resources {
v.Timeouts = nil
}
}

p := &schema.Provider{
Schema: map[string]*schema.Schema{
"subscription_id": {
Expand Down
10 changes: 7 additions & 3 deletions azurerm/resource_arm_resource_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"

"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources"
"github.com/hashicorp/terraform/helper/schema"
Expand Down Expand Up @@ -37,7 +38,8 @@ func resourceArmResourceGroup() *schema.Resource {

func resourceArmResourceGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).resource.GroupsClient
ctx := meta.(*ArmClient).StopContext
ctx, cancel := timeouts.ForCreateUpdate(meta.(*ArmClient).StopContext, d)
defer cancel()

name := d.Get("name").(string)
location := azure.NormalizeLocation(d.Get("location").(string))
Expand Down Expand Up @@ -77,7 +79,8 @@ func resourceArmResourceGroupCreateUpdate(d *schema.ResourceData, meta interface

func resourceArmResourceGroupRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).resource.GroupsClient
ctx := meta.(*ArmClient).StopContext
ctx, cancel := timeouts.ForRead(meta.(*ArmClient).StopContext, d)
defer cancel()

id, err := azure.ParseAzureResourceID(d.Id())
if err != nil {
Expand Down Expand Up @@ -106,7 +109,8 @@ func resourceArmResourceGroupRead(d *schema.ResourceData, meta interface{}) erro

func resourceArmResourceGroupDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).resource.GroupsClient
ctx := meta.(*ArmClient).StopContext
ctx, cancel := timeouts.ForDelete(meta.(*ArmClient).StopContext, d)
defer cancel()

id, err := azure.ParseAzureResourceID(d.Id())
if err != nil {
Expand Down

0 comments on commit d413ab8

Please sign in to comment.