Skip to content

Commit

Permalink
DWX-18732 Create and delete CDW AWS cluster object
Browse files Browse the repository at this point in the history
This commit adds the basic creation and deletion capabilities
- The model and schema for a data warehouse kubernetes cluster creation.
- The creation call
- The deletion call
- Some very basic unit tests
  • Loading branch information
tevesz committed Jul 17, 2024
1 parent fad90c0 commit 2137006
Show file tree
Hide file tree
Showing 7 changed files with 907 additions and 9 deletions.
46 changes: 46 additions & 0 deletions examples/resources/cdp_dw_aws_cluster/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## 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.

resource "cdp_dw_aws_cluster" "example" {
crn = cdp_datalake_aws_datalake.example.crn
name = "<value>"
cluster_id = "<value>"
node_role_cdw_managed_policy_arn = "<value>"
database_backup_retention_days = 7
custom_registry_options = var.custom_registry_options
custom_subdomain = ""
network_settings = {
worker_subnet_ids = ["<value>", "<value>", "<value>"]
load_balancer_subnet_ids = ["<value>", "<value>", "<value>"]
use_overlay_network = false
whitelist_k8s_cluster_access_ip_cidrs = ["0.0.0.0/0"]
whitelist_workload_access_ip_cidrs = ["0.0.0.0/0"]
use_private_load_balancer = true
use_public_worker_node = false
}
instance_settings = {
custom_ami_id = ""
enable_spot_instances = false
compute_instance_types = ["<value>"]
additional_instance_types = ["<value>"]
}
}

output "crn" {
value = cdp_dw_aws_cluster.example.crn
}

output "cluster_id" {
value = cdp_dw_aws_cluster.example.cluster_id
}

output "name" {
value = cdp_dw_aws_cluster.example.name
}
15 changes: 8 additions & 7 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@ import (
"runtime"
"strconv"

"github.com/cloudera/terraform-provider-cdp/resources/datahub"
"github.com/cloudera/terraform-provider-cdp/resources/de"
"github.com/cloudera/terraform-provider-cdp/resources/dw"
"github.com/cloudera/terraform-provider-cdp/resources/iam"
"github.com/cloudera/terraform-provider-cdp/resources/ml"
"github.com/cloudera/terraform-provider-cdp/resources/opdb"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
Expand All @@ -33,8 +26,15 @@ import (
"github.com/hashicorp/terraform-plugin-log/tflog"

"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/cdp"
"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"
"github.com/cloudera/terraform-provider-cdp/resources/environments"
"github.com/cloudera/terraform-provider-cdp/resources/iam"
"github.com/cloudera/terraform-provider-cdp/resources/ml"
"github.com/cloudera/terraform-provider-cdp/resources/opdb"
)

// Ensure the implementation satisfies the expected interfaces.
Expand Down Expand Up @@ -249,6 +249,7 @@ func (p *CdpProvider) Resources(_ context.Context) []func() resource.Resource {
ml.NewWorkspaceResource,
de.NewServiceResource,
dw.NewHiveResource,
dwaws.NewDwClusterResource,
}
}

Expand Down
5 changes: 3 additions & 2 deletions provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ import (
"testing"

"github.com/hashicorp/terraform-plugin-framework/datasource"
fwprovider "github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"

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

"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"
"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,6 +635,7 @@ func TestCdpProvider_Resources(t *testing.T) {
ml.NewWorkspaceResource,
de.NewServiceResource,
dw.NewHiveResource,
dwaws.NewDwClusterResource,
}

provider := CdpProvider{testVersion}
Expand Down
114 changes: 114 additions & 0 deletions resources/dw/cluster/aws/model_cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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 aws

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

"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/dw/models"
"github.com/cloudera/terraform-provider-cdp/utils"
)

type networkResourceModel struct {
WorkerSubnetIDs types.List `tfsdk:"worker_subnet_ids"`
LoadBalancerSubnetIDs types.List `tfsdk:"load_balancer_subnet_ids"`
UseOverlayNetwork types.Bool `tfsdk:"use_overlay_network"`
WhitelistK8sClusterAccessIPCIDRs types.List `tfsdk:"whitelist_k8s_cluster_access_ip_cidrs"`
WhitelistWorkloadAccessIPCIDRs types.List `tfsdk:"whitelist_workload_access_ip_cidrs"`
UsePrivateLoadBalancer types.Bool `tfsdk:"use_private_load_balancer"`
UsePublicWorkerNode types.Bool `tfsdk:"use_public_worker_node"`
}

type customRegistryOptions struct {
RegistryType types.String `tfsdk:"registry_type"`
RepositoryURL types.String `tfsdk:"repository_url"`
}

type instanceResourceModel struct {
CustomAmiID types.String `tfsdk:"custom_ami_id"`
EnableSpotInstances types.Bool `tfsdk:"enable_spot_instances"`
ComputeInstanceTypes types.List `tfsdk:"compute_instance_types"`
AdditionalInstanceTypes types.List `tfsdk:"additional_instance_types"`
}

type resourceModel struct {
ID types.String `tfsdk:"id"`
Crn types.String `tfsdk:"crn"`
Name types.String `tfsdk:"name"`
ClusterID types.String `tfsdk:"cluster_id"`
LastUpdated types.String `tfsdk:"last_updated"`
NodeRoleCDWManagedPolicyArn types.String `tfsdk:"node_role_cdw_managed_policy_arn"`
DatabaseBackupRetentionDays types.Int64 `tfsdk:"database_backup_retention_days"`
CustomRegistryOptions *customRegistryOptions `tfsdk:"custom_registry_options"`
CustomSubdomain types.String `tfsdk:"custom_subdomain"`
NetworkSettings *networkResourceModel `tfsdk:"network_settings"`
InstanceSettings *instanceResourceModel `tfsdk:"instance_settings"`
}

func (p *resourceModel) convertToCreateAwsClusterRequest() *models.CreateAwsClusterRequest {
return &models.CreateAwsClusterRequest{
EnvironmentCrn: p.Crn.ValueStringPointer(),
UseOverlayNetwork: p.NetworkSettings.UseOverlayNetwork.ValueBool(),
WhitelistK8sClusterAccessIPCIDRs: utils.FromListValueToStringList(p.NetworkSettings.WhitelistK8sClusterAccessIPCIDRs),
WhitelistWorkloadAccessIPCIDRs: utils.FromListValueToStringList(p.NetworkSettings.WhitelistWorkloadAccessIPCIDRs),
UsePrivateLoadBalancer: p.NetworkSettings.UsePrivateLoadBalancer.ValueBool(),
UsePublicWorkerNode: p.NetworkSettings.UsePublicWorkerNode.ValueBool(),
WorkerSubnetIds: utils.FromListValueToStringList(p.NetworkSettings.WorkerSubnetIDs),
LbSubnetIds: utils.FromListValueToStringList(p.NetworkSettings.LoadBalancerSubnetIDs),
NodeRoleCDWManagedPolicyArn: p.NodeRoleCDWManagedPolicyArn.ValueString(),
DatabaseBackupRetentionPeriod: utils.Int64To32Pointer(p.DatabaseBackupRetentionDays),
CustomSubdomain: p.CustomSubdomain.ValueString(),
CustomRegistryOptions: p.getCustomRegistryOptions(),
EnableSpotInstances: p.getEnableSpotInstances(),
CustomAmiID: p.getCustomAmiID(),
ComputeInstanceTypes: p.getComputeInstanceTypes(),
AdditionalInstanceTypes: p.getAdditionalInstanceTypes(),
}
}

func (p *resourceModel) getEnableSpotInstances() *bool {
if i := p.InstanceSettings; i != nil {
return i.EnableSpotInstances.ValueBoolPointer()
}
return nil
}

func (p *resourceModel) getCustomAmiID() string {
if i := p.InstanceSettings; i != nil {
return p.InstanceSettings.CustomAmiID.ValueString()
}
return ""
}

func (p *resourceModel) getComputeInstanceTypes() []string {
if i := p.InstanceSettings; i != nil {
return utils.FromListValueToStringList(p.InstanceSettings.ComputeInstanceTypes)
}
return nil
}

func (p *resourceModel) getAdditionalInstanceTypes() []string {
if i := p.InstanceSettings; i != nil {
return utils.FromListValueToStringList(p.InstanceSettings.AdditionalInstanceTypes)
}
return nil
}

func (p *resourceModel) getCustomRegistryOptions() *models.CustomRegistryOptions {
if cro := p.CustomRegistryOptions; cro != nil {
return &models.CustomRegistryOptions{
DisableImageVerification: true, // option will be deprecated, we should disallow image verification
RegistryType: p.CustomRegistryOptions.RegistryType.ValueString(),
RepositoryURL: p.CustomRegistryOptions.RepositoryURL.ValueString(),
}
}
return nil
}
136 changes: 136 additions & 0 deletions resources/dw/cluster/aws/resource_cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// 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 aws

import (
"context"
"time"

"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"

"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/cdp"
"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/dw/client/operations"
"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/dw/models"
"github.com/cloudera/terraform-provider-cdp/utils"
)

type dwClusterResource struct {
client *cdp.Client
}

var (
_ resource.Resource = (*dwClusterResource)(nil)
_ resource.ResourceWithConfigure = (*dwClusterResource)(nil)
)

func NewDwClusterResource() resource.Resource {
return &dwClusterResource{}
}

func (r *dwClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
r.client = utils.GetCdpClientForResource(req, resp)
}

func (r *dwClusterResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_dw_aws_cluster"
}

func (r *dwClusterResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = dwClusterSchema
}

func (r *dwClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
// Retrieve values from plan
var plan resourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

// Generate API request body from plan
clusterParams := operations.NewCreateAwsClusterParamsWithContext(ctx).
WithInput(plan.convertToCreateAwsClusterRequest())

// Create new aws cluster
response, err := r.client.Dw.Operations.CreateAwsCluster(clusterParams)
if err != nil {
resp.Diagnostics.AddError(
"Error creating data warehouse aws cluster",
"Could not create cluster, unexpected error: "+err.Error(),
)
return
}

payload := response.GetPayload()
desc := operations.NewDescribeClusterParamsWithContext(ctx).
WithInput(&models.DescribeClusterRequest{ClusterID: &payload.ClusterID})
describe, err := r.client.Dw.Operations.DescribeCluster(desc)
if err != nil {
resp.Diagnostics.AddError(
"Error creating data warehouse aws cluster",
"Could not describe cluster, unexpected error: "+err.Error(),
)
return
}

cluster := describe.GetPayload()

// Map response body to schema and populate Computed attribute values
plan.ID = types.StringValue(cluster.Cluster.EnvironmentCrn)
plan.Crn = types.StringValue(cluster.Cluster.EnvironmentCrn)
plan.Name = types.StringValue(cluster.Cluster.Name)
plan.ClusterID = types.StringValue(cluster.Cluster.ID)
plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850))

// Set state to fully populated data
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

}

func (r *dwClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
tflog.Warn(ctx, "Read operation is not implemented yet.")
}

func (r *dwClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
tflog.Warn(ctx, "Update operation is not implemented yet.")
}

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

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

if resp.Diagnostics.HasError() {
return
}

op := operations.NewDeleteClusterParamsWithContext(ctx).
WithInput(&models.DeleteClusterRequest{
ClusterID: state.ClusterID.ValueStringPointer(),
// Force: true,
})

if _, err := r.client.Dw.Operations.DeleteCluster(op); err != nil {
resp.Diagnostics.AddError(
"Error deleting data warehouse aws cluster",
"Could not delete cluster, unexpected error: "+err.Error(),
)
return
}
}
Loading

0 comments on commit 2137006

Please sign in to comment.