Skip to content

Commit

Permalink
COD-4084 Add polling_options for COD deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
richardantal authored and gregito committed Jul 18, 2024
1 parent fad90c0 commit a4aa674
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 31 deletions.
19 changes: 18 additions & 1 deletion resources/opdb/common_scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ package opdb

import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
Expand All @@ -22,9 +24,24 @@ var generalAttributes = map[string]schema.Attribute{
MarkdownDescription: "Polling related configuration options that could specify various values that will be used during CDP resource creation.",
Optional: true,
Attributes: map[string]schema.Attribute{
"async": schema.BoolAttribute{
MarkdownDescription: "Boolean value that specifies if Terraform should wait for resource creation/deletion.",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false),
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.UseStateForUnknown(),
},
},
"polling_timeout": schema.Int64Attribute{
MarkdownDescription: "Timeout value in minutes that specifies for how long should the polling go for resource creation/deletion.",
Default: int64default.StaticInt64(60),
Default: int64default.StaticInt64(90),
Computed: true,
Optional: true,
},
"call_failure_threshold": schema.Int64Attribute{
MarkdownDescription: "Threshold value that specifies how many times should a single call failure happen before giving up the polling.",
Default: int64default.StaticInt64(3),
Computed: true,
Optional: true,
},
Expand Down
1 change: 1 addition & 0 deletions resources/opdb/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ const internalServerErrorRetryQuantity = 4
const pollingInterval = 30 * time.Second
const pollingTimeout = 2 * time.Hour
const pollingDelay = 15 * time.Second
const callFailureThreshold = 3

var failedStatusKeywords = []string{"FAILED", "DELETE_FAILED", "MISSING", "CREATE_FAILED", "DELETED"}
18 changes: 16 additions & 2 deletions resources/opdb/polling.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package opdb

import (
"context"
"errors"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
Expand All @@ -31,6 +32,11 @@ func waitForToBeAvailable(dataBaseName string, environmentName string, client *c
if err != nil {
return "", err
}
failureThreshold, failureThresholdErr := utils.CalculateCallFailureThresholdOrDefault(ctx, options, callFailureThreshold)
if failureThresholdErr != nil {
return "", failureThresholdErr
}
callFailedCount := 0
stateConf := &retry.StateChangeConf{
Pending: []string{},
Target: []string{"AVAILABLE"},
Expand All @@ -44,11 +50,18 @@ func waitForToBeAvailable(dataBaseName string, environmentName string, client *c
if err != nil {
if isNotFoundError(err) {
tflog.Debug(ctx, fmt.Sprintf("Recoverable error describing Database: %s", err))
callFailedCount = 0
return nil, "", nil
}
tflog.Debug(ctx, fmt.Sprintf("Error describing Database: %s", err))
callFailedCount++
if callFailedCount <= failureThreshold {
tflog.Warn(ctx, fmt.Sprintf("Error describing cluster with call failure due to [%s] but threshold limit is not reached yet (%d out of %d).", err.Error(), callFailedCount, callFailureThreshold))
return nil, "", nil
}
tflog.Error(ctx, fmt.Sprintf("Error describing Database (due to: %s) and call failure threshold limit exceeded.", err))
return nil, "", err
}
callFailedCount = 0
tflog.Debug(ctx, fmt.Sprintf("Described Database: %s", resp.GetPayload().DatabaseDetails.Status))
intf, st, e := checkIfDatabaseCreationFailed(resp)
tflog.Debug(ctx, fmt.Sprintf("Updating returning status from '%s' to '%s'", status, st))
Expand Down Expand Up @@ -76,7 +89,8 @@ func waitForToBeDeleted(dataBaseName string, environmentName string, client *cli
resp, err := describeWithRecover(dataBaseName, environmentName, client, ctx)
if err != nil {
tflog.Debug(ctx, fmt.Sprintf("Error describing Database: %s", err))
if envErr, ok := err.(*operations.DescribeDatabaseDefault); ok {
var envErr *operations.DescribeDatabaseDefault
if errors.As(err, &envErr) {
if cdp.IsDatabaseError(envErr.GetPayload(), "NOT_FOUND", "") {
return nil, "", nil
}
Expand Down
65 changes: 37 additions & 28 deletions resources/opdb/resource_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,21 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
return
}

status, err := waitForToBeAvailable(data.DatabaseName.ValueString(), data.Environment.ValueString(), r.client.Opdb, ctx, data.PollingOptions)
tflog.Debug(ctx, fmt.Sprintf("Database polling finished, setting status from '%s' to '%s'", data.Status.ValueString(), status))
data.Status = types.StringValue(status)
diags = resp.State.Set(ctx, data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if err != nil {
tflog.Debug(ctx, fmt.Sprintf("Cluster creation has ended up in error: %s", err.Error()))
utils.AddDatabaseDiagnosticsError(err, &resp.Diagnostics, "create Database")
return
if !(data.PollingOptions != nil && data.PollingOptions.Async.ValueBool()) {
status, err := waitForToBeAvailable(data.DatabaseName.ValueString(), data.Environment.ValueString(), r.client.Opdb, ctx, data.PollingOptions)
tflog.Debug(ctx, fmt.Sprintf("Database polling finished, setting status from '%s' to '%s'", data.Status.ValueString(), status))
data.Status = types.StringValue(status)
diags = resp.State.Set(ctx, data)
resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}
if err != nil {
tflog.Debug(ctx, fmt.Sprintf("Cluster creation has ended up in error: %s", err.Error()))
utils.AddDatabaseDiagnosticsError(err, &resp.Diagnostics, "create Database")
return
}
}
}

Expand Down Expand Up @@ -177,18 +180,21 @@ func (r *databaseResource) Update(ctx context.Context, req resource.UpdateReques
return
}

status, err := waitForToBeAvailable(data.DatabaseName.ValueString(), data.Environment.ValueString(), r.client.Opdb, ctx, data.PollingOptions)
tflog.Debug(ctx, fmt.Sprintf("Database polling finished, setting status from '%s' to '%s'", data.Status.ValueString(), status))
data.Status = types.StringValue(status)
diags = resp.State.Set(ctx, data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if err != nil {
tflog.Debug(ctx, fmt.Sprintf("Cluster update has ended up in error: %s", err.Error()))
utils.AddDatabaseDiagnosticsError(err, &resp.Diagnostics, "update Database")
return
if !(data.PollingOptions != nil && data.PollingOptions.Async.ValueBool()) {
status, err := waitForToBeAvailable(data.DatabaseName.ValueString(), data.Environment.ValueString(), r.client.Opdb, ctx, data.PollingOptions)
tflog.Debug(ctx, fmt.Sprintf("Database polling finished, setting status from '%s' to '%s'", data.Status.ValueString(), status))
data.Status = types.StringValue(status)
diags = resp.State.Set(ctx, data)
resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}
if err != nil {
tflog.Debug(ctx, fmt.Sprintf("Cluster update has ended up in error: %s", err.Error()))
utils.AddDatabaseDiagnosticsError(err, &resp.Diagnostics, "update Database")
return
}
}
}

Expand All @@ -215,9 +221,12 @@ func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteReques
return
}

err = waitForToBeDeleted(state.DatabaseName.ValueString(), state.Environment.ValueString(), r.client.Opdb, ctx, state.PollingOptions)
if err != nil {
utils.AddDatabaseDiagnosticsError(err, &resp.Diagnostics, "delete database")
return
if !(state.PollingOptions != nil && state.PollingOptions.Async.ValueBool()) {
err = waitForToBeDeleted(state.DatabaseName.ValueString(), state.Environment.ValueString(), r.client.Opdb, ctx, state.PollingOptions)

if err != nil {
utils.AddDatabaseDiagnosticsError(err, &resp.Diagnostics, "delete database")
return
}
}
}

0 comments on commit a4aa674

Please sign in to comment.