Skip to content

Commit e29fda3

Browse files
authored
feat: Long-running operation improvements for mongodbatlas_privatelink_endpoint resource (#3543)
* implement delete_on_create_timeout * rename files * add forcenew * correct doc * make sure delete on create is finished * Revert "make sure delete on create is finished" This reverts commit 31ce923. * reuse project to avoid issues on project deletion
1 parent a7e5f68 commit e29fda3

File tree

7 files changed

+69
-13
lines changed

7 files changed

+69
-13
lines changed

.changelog/3543.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
resource/mongodbatlas_privatelink_endpoint: Adds `delete_on_create_timeout` attribute to indicate whether to delete the resource if its creation times out
3+
```

docs/resources/privatelink_endpoint.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ resource "mongodbatlas_privatelink_endpoint" "test" {
4040
* `region` - (Required) Cloud provider region in which you want to create the private endpoint connection.
4141
Accepted values are: [AWS regions](https://docs.atlas.mongodb.com/reference/amazon-aws/#amazon-aws), [AZURE regions](https://docs.atlas.mongodb.com/reference/microsoft-azure/#microsoft-azure) and [GCP regions](https://docs.atlas.mongodb.com/reference/google-gcp/#std-label-google-gcp)
4242
* `timeouts`- (Optional) The duration of time to wait for Private Endpoint to be created or deleted. The timeout value is defined by a signed sequence of decimal numbers with a time unit suffix such as: `1h45m`, `300s`, `10m`, etc. The valid time units are: `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. The default timeout for Private Endpoint create & delete is `1h`. Learn more about timeouts [here](https://www.terraform.io/plugin/sdkv2/resources/retries-and-customizable-timeouts).
43-
43+
* `delete_on_create_timeout` - (Optional) Flag that indicates whether to delete the resource if creation times out. Default is `true`. When Terraform apply fails, it returns immediately without waiting for cleanup to complete. If you suspect a transient error, wait before retrying to allow resource deletion to finish.
4444

4545
## Attributes Reference
4646

internal/service/privatelinkendpoint/resource_privatelink_endpoint.go renamed to internal/service/privatelinkendpoint/resource.go

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
1313
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1414
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
15+
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/cleanup"
1516
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion"
1617
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/validate"
1718
"github.com/mongodb/terraform-provider-mongodbatlas/internal/config"
@@ -23,13 +24,15 @@ const (
2324
errorPrivateLinkEndpointsRead = "error reading MongoDB Private Endpoints Connection(%s): %s"
2425
errorPrivateLinkEndpointsDelete = "error deleting MongoDB Private Endpoints Connection(%s): %s"
2526
ErrorPrivateLinkEndpointsSetting = "error setting `%s` for MongoDB Private Endpoints Connection(%s): %s"
27+
oneMinute = 1 * time.Minute
28+
delayAndMinTimeout = 5 * time.Second
2629
)
2730

2831
func Resource() *schema.Resource {
2932
return &schema.Resource{
30-
CreateContext: resourceCreate,
31-
ReadContext: resourceRead,
32-
DeleteContext: resourceDelete,
33+
CreateWithoutTimeout: resourceCreate,
34+
ReadWithoutTimeout: resourceRead,
35+
DeleteWithoutTimeout: resourceDelete,
3336
Importer: &schema.ResourceImporter{
3437
StateContext: resourceImport,
3538
},
@@ -106,6 +109,12 @@ func Resource() *schema.Resource {
106109
Type: schema.TypeString,
107110
},
108111
},
112+
"delete_on_create_timeout": { // Don't use Default: true to avoid unplanned changes when upgrading from previous versions.
113+
Type: schema.TypeBool,
114+
Optional: true,
115+
ForceNew: true,
116+
Description: "Flag that indicates whether to delete the resource if creation times out. Default is true.",
117+
},
109118
},
110119
Timeouts: &schema.ResourceTimeout{
111120
Create: schema.DefaultTimeout(1 * time.Hour),
@@ -134,15 +143,23 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.
134143
Pending: []string{"INITIATING", "DELETING"},
135144
Target: []string{"WAITING_FOR_USER", "FAILED", "DELETED", "AVAILABLE"},
136145
Refresh: refreshFunc(ctx, connV2, projectID, providerName, privateEndpoint.GetId()),
137-
Timeout: d.Timeout(schema.TimeoutCreate) - time.Minute, // If using a CRUD function with a timeout, any StateChangeConf timeouts should be configured below that duration to avoid returning the SDK context: deadline exceeded error instead of the retry logic error.
138-
MinTimeout: 5 * time.Second,
139-
Delay: 3 * time.Second,
146+
Timeout: d.Timeout(schema.TimeoutCreate) - oneMinute, // If using a CRUD function with a timeout, any StateChangeConf timeouts should be configured below that duration to avoid returning the SDK context: deadline exceeded error instead of the retry logic error.
147+
MinTimeout: delayAndMinTimeout,
148+
Delay: delayAndMinTimeout,
140149
}
141150

142151
// Wait, catching any errors
143-
_, err = stateConf.WaitForStateContext(ctx)
144-
if err != nil {
145-
return diag.FromErr(fmt.Errorf(errorPrivateLinkEndpointsCreate, err))
152+
_, errWait := stateConf.WaitForStateContext(ctx)
153+
deleteOnCreateTimeout := true // default value when not set
154+
if v, ok := d.GetOkExists("delete_on_create_timeout"); ok {
155+
deleteOnCreateTimeout = v.(bool)
156+
}
157+
errWait = cleanup.HandleCreateTimeout(deleteOnCreateTimeout, errWait, func(ctxCleanup context.Context) error {
158+
_, errCleanup := connV2.PrivateEndpointServicesApi.DeletePrivateEndpointService(ctxCleanup, projectID, providerName, privateEndpoint.GetId()).Execute()
159+
return errCleanup
160+
})
161+
if errWait != nil {
162+
return diag.FromErr(fmt.Errorf(errorPrivateLinkEndpointsCreate, errWait))
146163
}
147164

148165
d.SetId(conversion.EncodeStateID(map[string]string{
@@ -255,10 +272,10 @@ func resourceDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.
255272
Target: []string{"DELETED", "FAILED"},
256273
Refresh: refreshFunc(ctx, connV2, projectID, providerName, privateLinkID),
257274
Timeout: d.Timeout(schema.TimeoutDelete),
258-
MinTimeout: 5 * time.Second,
259-
Delay: 3 * time.Second,
275+
MinTimeout: delayAndMinTimeout,
276+
Delay: delayAndMinTimeout,
260277
}
261-
// Wait, catching any errors
278+
262279
_, err = stateConf.WaitForStateContext(ctx)
263280
if err != nil {
264281
return diag.FromErr(fmt.Errorf(errorPrivateLinkEndpointsDelete, privateLinkID, err))

internal/service/privatelinkendpoint/resource_privatelink_endpoint_test.go renamed to internal/service/privatelinkendpoint/resource_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"regexp"
78
"testing"
89

910
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
@@ -117,6 +118,41 @@ func TestAccNetworkRSPrivateLinkEndpointGCP_basic(t *testing.T) {
117118
})
118119
}
119120

121+
func TestAccPrivateLinkEndpoint_deleteOnCreateTimeout(t *testing.T) {
122+
var (
123+
projectID = acc.ProjectIDExecution(t)
124+
region = "us-east-1"
125+
providerName = "AWS"
126+
)
127+
128+
resource.ParallelTest(t, resource.TestCase{
129+
PreCheck: func() { acc.PreCheckBasic(t) },
130+
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
131+
CheckDestroy: checkDestroy,
132+
Steps: []resource.TestStep{
133+
{
134+
Config: configDeleteOnCreateTimeout(projectID, providerName, region, "1s", true),
135+
ExpectError: regexp.MustCompile("will run cleanup because delete_on_create_timeout is true"),
136+
},
137+
},
138+
})
139+
}
140+
141+
func configDeleteOnCreateTimeout(projectID, providerName, region, timeout string, deleteOnTimeout bool) string {
142+
return fmt.Sprintf(`
143+
resource "mongodbatlas_privatelink_endpoint" "test" {
144+
project_id = %[1]q
145+
provider_name = %[2]q
146+
region = %[3]q
147+
delete_on_create_timeout = %[5]t
148+
149+
timeouts {
150+
create = %[4]q
151+
}
152+
}
153+
`, projectID, providerName, region, timeout, deleteOnTimeout)
154+
}
155+
120156
func importStateIDFunc(resourceName string) resource.ImportStateIdFunc {
121157
return func(s *terraform.State) (string, error) {
122158
rs, ok := s.RootModule().Resources[resourceName]

0 commit comments

Comments
 (0)