Skip to content

Commit

Permalink
DWX-18764 Wait for creation to finish for hive
Browse files Browse the repository at this point in the history
The hive virtual warehouse creation and deletion is awaited.
  • Loading branch information
tevesz authored and gregito committed Oct 31, 2024
1 parent 41ca13d commit 38e4223
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 93 deletions.
4 changes: 2 additions & 2 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ import (
"github.com/cloudera/terraform-provider-cdp/resources/datahub"
"github.com/cloudera/terraform-provider-cdp/resources/datalake"
"github.com/cloudera/terraform-provider-cdp/resources/de"
"github.com/cloudera/terraform-provider-cdp/resources/dw"
dwaws "github.com/cloudera/terraform-provider-cdp/resources/dw/cluster/aws"
dwdatabasecatalog "github.com/cloudera/terraform-provider-cdp/resources/dw/databasecatalog"
"github.com/cloudera/terraform-provider-cdp/resources/dw/virtualwarehouse/hive"
"github.com/cloudera/terraform-provider-cdp/resources/environments"
"github.com/cloudera/terraform-provider-cdp/resources/iam"
"github.com/cloudera/terraform-provider-cdp/resources/ml"
Expand Down Expand Up @@ -249,7 +249,7 @@ func (p *CdpProvider) Resources(_ context.Context) []func() resource.Resource {
opdb.NewDatabaseResource,
ml.NewWorkspaceResource,
de.NewServiceResource,
dw.NewHiveResource,
hive.NewHiveResource,
dwaws.NewDwClusterResource,
dwdatabasecatalog.NewDwDatabaseCatalogResource,
}
Expand Down
4 changes: 2 additions & 2 deletions provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ import (
"github.com/cloudera/terraform-provider-cdp/resources/datahub"
"github.com/cloudera/terraform-provider-cdp/resources/datalake"
"github.com/cloudera/terraform-provider-cdp/resources/de"
"github.com/cloudera/terraform-provider-cdp/resources/dw"
dwaws "github.com/cloudera/terraform-provider-cdp/resources/dw/cluster/aws"
dwdatabasecatalog "github.com/cloudera/terraform-provider-cdp/resources/dw/databasecatalog"
"github.com/cloudera/terraform-provider-cdp/resources/dw/virtualwarehouse/hive"
"github.com/cloudera/terraform-provider-cdp/resources/environments"
"github.com/cloudera/terraform-provider-cdp/resources/iam"
"github.com/cloudera/terraform-provider-cdp/resources/ml"
Expand Down Expand Up @@ -635,7 +635,7 @@ func TestCdpProvider_Resources(t *testing.T) {
opdb.NewDatabaseResource,
ml.NewWorkspaceResource,
de.NewServiceResource,
dw.NewHiveResource,
hive.NewHiveResource,
dwaws.NewDwClusterResource,
dwdatabasecatalog.NewDwDatabaseCatalogResource,
}
Expand Down
20 changes: 0 additions & 20 deletions resources/dw/model_hive_vw.go

This file was deleted.

46 changes: 0 additions & 46 deletions resources/dw/schema_hive_vw.go

This file was deleted.

44 changes: 44 additions & 0 deletions resources/dw/virtualwarehouse/hive/model_hive_vw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2024 Cloudera. All Rights Reserved.
//
// This file is licensed under the Apache License Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
//
// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, either express or implied. Refer to the License for the specific
// permissions and limitations governing your use of the file.

package hive

import (
"time"

"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/cloudera/terraform-provider-cdp/utils"
)

type resourceModel struct {
ID types.String `tfsdk:"id"`
ClusterID types.String `tfsdk:"cluster_id"`
DatabaseCatalogID types.String `tfsdk:"database_catalog_id"`
Name types.String `tfsdk:"name"`
LastUpdated types.String `tfsdk:"last_updated"`
Status types.String `tfsdk:"status"`
PollingOptions *utils.PollingOptions `tfsdk:"polling_options"`
}

// TODO these are the same everywhere, abstract this
func (p *resourceModel) getPollingTimeout() time.Duration {
if p.PollingOptions != nil {
return time.Duration(p.PollingOptions.PollingTimeout.ValueInt64()) * time.Minute
}
return 40 * time.Minute
}

func (p *resourceModel) getCallFailureThreshold() int {
if p.PollingOptions != nil {
return int(p.PollingOptions.CallFailureThreshold.ValueInt64())
}
return 3
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@
// OF ANY KIND, either express or implied. Refer to the License for the specific
// permissions and limitations governing your use of the file.

package dw
package hive

import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand Down Expand Up @@ -50,7 +54,7 @@ func (r *hiveResource) Schema(_ context.Context, _ resource.SchemaRequest, resp

func (r *hiveResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
// Retrieve values from plan
var plan hiveResourceModel
var plan resourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
Expand All @@ -62,7 +66,7 @@ func (r *hiveResource) Create(ctx context.Context, req resource.CreateRequest, r
WithInput(&models.CreateVwRequest{
Name: plan.Name.ValueStringPointer(),
ClusterID: plan.ClusterID.ValueStringPointer(),
DbcID: plan.DbCatalogID.ValueStringPointer(),
DbcID: plan.DatabaseCatalogID.ValueStringPointer(),
VwType: models.VwTypeHive.Pointer(),
})

Expand All @@ -77,8 +81,29 @@ func (r *hiveResource) Create(ctx context.Context, req resource.CreateRequest, r
}

payload := response.GetPayload()
clusterID := plan.ClusterID.ValueStringPointer()
vwID := &payload.VwID

if opts := plan.PollingOptions; !(opts != nil && opts.Async.ValueBool()) {
callFailedCount := 0
stateConf := &retry.StateChangeConf{
Pending: []string{"Accepted", "Creating", "Created", "Starting"},
Target: []string{"Running"},
Delay: 30 * time.Second,
Timeout: plan.getPollingTimeout(),
PollInterval: 30 * time.Second,
Refresh: r.stateRefresh(ctx, clusterID, vwID, &callFailedCount, plan.getCallFailureThreshold()),
}
if _, err = stateConf.WaitForStateContext(ctx); err != nil {
resp.Diagnostics.AddError(
"Error waiting for Data Warehouse hive virtual warehouse",
"Could not create hive, unexpected error: "+err.Error(),
)
return
}
}
desc := operations.NewDescribeVwParamsWithContext(ctx).
WithInput(&models.DescribeVwRequest{VwID: &payload.VwID, ClusterID: plan.ClusterID.ValueStringPointer()})
WithInput(&models.DescribeVwRequest{VwID: vwID, ClusterID: clusterID})
describe, err := r.client.Dw.Operations.DescribeVw(desc)
if err != nil {
resp.Diagnostics.AddError(
Expand All @@ -89,13 +114,11 @@ func (r *hiveResource) Create(ctx context.Context, req resource.CreateRequest, r
}

hive := describe.GetPayload()

// Map response body to schema and populate Computed attribute values
plan.ID = types.StringValue(hive.Vw.ID)
plan.DbCatalogID = types.StringValue(hive.Vw.DbcID)
plan.DatabaseCatalogID = types.StringValue(hive.Vw.DbcID)
plan.Name = types.StringValue(hive.Vw.Name)

// Set state to fully populated data
plan.Status = types.StringValue(hive.Vw.Status)
plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850))
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
}
Expand All @@ -109,26 +132,75 @@ func (r *hiveResource) Update(ctx context.Context, req resource.UpdateRequest, r
}

func (r *hiveResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state hiveResourceModel
var state resourceModel

// Read Terraform prior state into the model
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)

if resp.Diagnostics.HasError() {
return
}

clusterID := state.ClusterID.ValueStringPointer()
vwID := state.ID.ValueStringPointer()
op := operations.NewDeleteVwParamsWithContext(ctx).
WithInput(&models.DeleteVwRequest{
ClusterID: state.ClusterID.ValueStringPointer(),
VwID: state.ID.ValueStringPointer(),
ClusterID: clusterID,
VwID: vwID,
})

if _, err := r.client.Dw.Operations.DeleteVw(op); err != nil {
if strings.Contains(err.Error(), "Virtual Warehouse not found") {
return
}
resp.Diagnostics.AddError(
"Error deleting Hive Virtual Warehouse",
"Could not delete Hive Virtual Warehouse, unexpected error: "+err.Error(),
)
return
}

if opts := state.PollingOptions; !(opts != nil && opts.Async.ValueBool()) {
callFailedCount := 0
stateConf := &retry.StateChangeConf{
Pending: []string{"Deleting", "Running", "Stopping", "Stopped", "Creating", "Created", "Starting", "Updating"},
Target: []string{"Deleted"}, // This is not an actual state, we added it to fake the state change
Delay: 30 * time.Second,
Timeout: state.getPollingTimeout(),
PollInterval: 30 * time.Second,
Refresh: r.stateRefresh(ctx, clusterID, vwID, &callFailedCount, state.getCallFailureThreshold()),
}
if _, err := stateConf.WaitForStateContext(ctx); err != nil {
resp.Diagnostics.AddError(
"Error waiting for Data Warehouse Hive Virtual Warehouse",
"Could not delete hive, unexpected error: "+err.Error(),
)
return
}
}
}

func (r *hiveResource) stateRefresh(ctx context.Context, clusterID *string, vwID *string, callFailedCount *int, callFailureThreshold int) func() (any, string, error) {
return func() (any, string, error) {
tflog.Debug(ctx, "About to describe hive")
params := operations.NewDescribeVwParamsWithContext(ctx).
WithInput(&models.DescribeVwRequest{ClusterID: clusterID, VwID: vwID})
resp, err := r.client.Dw.Operations.DescribeVw(params)
if err != nil {
if strings.Contains(err.Error(), "Virtual Warehouse not found") {
return &models.DescribeVwResponse{}, "Deleted", nil
}
*callFailedCount++
if *callFailedCount <= callFailureThreshold {
tflog.Warn(ctx, fmt.Sprintf("could not describe Data Warehouse Hive Virtual Warehouse "+
"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 Data Warehouse Hive Virtual Warehouse due to [%s] "+
"failure threshold limit exceeded.", err.Error()))
return nil, "", err
}
*callFailedCount = 0
vw := resp.GetPayload()
tflog.Debug(ctx, fmt.Sprintf("Described Hive %s with status %s", vw.Vw.ID, vw.Vw.Status))
return vw, vw.Vw.Status, nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// OF ANY KIND, either express or implied. Refer to the License for the specific
// permissions and limitations governing your use of the file.

package dw_test
package hive_test

import (
"context"
Expand Down Expand Up @@ -67,8 +67,6 @@ func TestAccHive_basic(t *testing.T) {
resource.TestCheckResourceAttr("cdp_vw_hive.test_hive", "database_catalog_id", params.DatabaseCatalogID),
),
},
// TODO ImportState testing
// TODO Update and Read testing
// Delete testing automatically occurs in TestCase
},
})
Expand Down Expand Up @@ -100,7 +98,7 @@ func testCheckHiveDestroy(s *terraform.State) error {

_, err := cdpClient.Dw.Operations.DescribeVw(params)
if err != nil {
if strings.Contains(err.Error(), "404") {
if strings.Contains(err.Error(), "Virtual Warehouse not found") {
continue
}
return err
Expand Down
Loading

0 comments on commit 38e4223

Please sign in to comment.