Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDPCP-13058 - Allow in-place update of AWS credential #176

Merged
merged 1 commit into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ linters-settings:

output:
format: colored-line-number
print-issued-lines: false
print-issued-lines: true

issues:
max-issues-per-linter: 0
Expand Down
42 changes: 42 additions & 0 deletions docs/resources/dw_database_catalog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "cdp_dw_database_catalog Resource - terraform-provider-cdp"
subcategory: ""
description: |-
Creates an AWS Data Warehouse database catalog.
---

# cdp_dw_database_catalog (Resource)

Creates an AWS Data Warehouse database catalog.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `cluster_id` (String) The unique identifier of the cluster.

### Optional

- `polling_options` (Attributes) Polling related configuration options that could specify various values that will be used during CDP resource creation. (see [below for nested schema](#nestedatt--polling_options))

### Read-Only

- `id` (String) The ID of this resource.
- `last_updated` (String) Timestamp of the last Terraform update of the order.
- `name` (String) The name of the database catalog.
- `status` (String) The status of the database catalog.

<a id="nestedatt--polling_options"></a>
### Nested Schema for `polling_options`

Optional:

- `async` (Boolean) Boolean value that specifies if Terraform should wait for resource creation/deletion.
- `call_failure_threshold` (Number) Threshold value that specifies how many times should a single call failure happen before giving up the polling.
- `polling_timeout` (Number) Timeout value in minutes that specifies for how long should the polling go for resource creation/deletion.


2 changes: 2 additions & 0 deletions docs/resources/environments_aws_credential.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ output "crn" {
### Optional

- `description` (String)
- `skip_org_policy_decisions` (Boolean) Whether to skip organizational policy decision checks or not.
- `verify_permissions` (Boolean) Whether to verify permissions upon saving or not.

### Read-Only

Expand Down
1 change: 1 addition & 0 deletions docs/resources/environments_aws_environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ output "crn" {

### Optional

- `cascading_delete` (Boolean)
- `create_private_subnets` (Boolean)
- `create_service_endpoints` (Boolean)
- `description` (String)
Expand Down
1 change: 1 addition & 0 deletions docs/resources/environments_azure_environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ output "crn" {

### Optional

- `cascading_delete` (Boolean)
- `create_private_endpoints` (Boolean)
- `description` (String)
- `enable_outbound_load_balancer` (Boolean)
Expand Down
1 change: 1 addition & 0 deletions docs/resources/environments_gcp_environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ output "shared_project_id" {
### Optional

- `availability_zones` (List of String) The zones of the environment in the given region. Multi-zone selection is not supported in GCP yet. It accepts only one zone until support is added.
- `cascading_delete` (Boolean)
- `description` (String) A description of the environment.
- `enable_tunnel` (Boolean) Whether to enable SSH tunneling for the environment.
- `encryption_key` (String) Key Resource ID of the customer managed encryption key to encrypt GCP resources.
Expand Down
23 changes: 23 additions & 0 deletions resources/environments/model_aws_credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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 environments

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

type awsCredentialResourceModel struct {
ID types.String `tfsdk:"id"`
Crn types.String `tfsdk:"crn"`
RoleArn types.String `tfsdk:"role_arn"`
Description types.String `tfsdk:"description"`
CredentialName types.String `tfsdk:"credential_name"`
VerifyPermissions types.Bool `tfsdk:"verify_permissions"`
SkipOrgPolicyDecisions types.Bool `tfsdk:"skip_org_policy_decisions"`
}
109 changes: 59 additions & 50 deletions resources/environments/resource_aws_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ package environments

import (
"context"
"errors"
"time"

"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"

"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/cdp"
environmentsclient "github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/environments/client"
"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/environments/client/operations"
environmentsmodels "github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/environments/models"
"github.com/cloudera/terraform-provider-cdp/utils"
Expand All @@ -42,14 +42,6 @@ type awsCredentialResource struct {
client *cdp.Client
}

type awsCredentialResourceModel struct {
ID types.String `tfsdk:"id"`
CredentialName types.String `tfsdk:"credential_name"`
RoleArn types.String `tfsdk:"role_arn"`
Crn types.String `tfsdk:"crn"`
Description types.String `tfsdk:"description"`
}

func NewAwsCredentialResource() resource.Resource {
return &awsCredentialResource{}
}
Expand All @@ -58,44 +50,6 @@ func (r *awsCredentialResource) Metadata(_ context.Context, req resource.Metadat
resp.TypeName = req.ProviderTypeName + "_environments_aws_credential"
}

func (r *awsCredentialResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "The AWS credential is used for authorization to provision resources such as compute instances within your cloud provider account.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"credential_name": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"role_arn": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"description": schema.StringAttribute{
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplaceIfConfigured(),
},
},
"crn": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
}
}

func (r *awsCredentialResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
r.client = utils.GetCdpClientForResource(req, resp)
}
Expand Down Expand Up @@ -137,6 +91,17 @@ func (r *awsCredentialResource) Create(ctx context.Context, req resource.CreateR
return
}

// AWS credential creation does not support a couple of fields to be set during creation but can be
// updated afterward thus if they are set in the plan, we need to update the credential right away
if credentialNeedsToBeUpdatedAfterCreation(plan) {
tflog.Debug(ctx, "Updating AWS credential required due to plan configuration")
updateErr := r.updateCredential(ctx, client, plan)
if updateErr != nil {
utils.AddEnvironmentDiagnosticsError(updateErr, &resp.Diagnostics, "update AWS Credential")
return
}
}

// Save plan into Terraform state
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
Expand Down Expand Up @@ -201,7 +166,22 @@ func FindCredentialByName(ctx context.Context, cdpClient *cdp.Client, credential
}

func (r *awsCredentialResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
// There is no UpdateAWSCredential API in CDP.
var plan awsCredentialResourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

client := r.client.Environments

err := r.updateCredential(ctx, client, plan)

if err != nil {
utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "update AWS Credential")
return
}
resp.State.Set(ctx, plan)
}

func (r *awsCredentialResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
Expand All @@ -226,3 +206,32 @@ func (r *awsCredentialResource) Delete(ctx context.Context, req resource.DeleteR
func (r *awsCredentialResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

func (r *awsCredentialResource) updateCredential(ctx context.Context, client *environmentsclient.Environments, plan awsCredentialResourceModel) error {
params := operations.NewUpdateAwsCredentialParamsWithContext(ctx)
params.WithInput(&environmentsmodels.UpdateAwsCredentialRequest{
RoleArn: plan.RoleArn.ValueStringPointer(),
Description: plan.Description.ValueString(),
CredentialName: plan.CredentialName.ValueStringPointer(),
VerifyPermissions: plan.VerifyPermissions.ValueBoolPointer(),
SkipOrgPolicyDecisions: plan.SkipOrgPolicyDecisions.ValueBoolPointer(),
})
return retry.RetryContext(ctx, credentialCreateRetryDuration, func() *retry.RetryError {
tflog.Debug(ctx, "Updating AWS credential")
_, err := client.Operations.UpdateAwsCredential(params)
if err != nil {
var envErr *operations.UpdateAwsCredentialDefault
if errors.As(err, &envErr) {
if utils.IsRetryableError(envErr.Code()) {
return retry.RetryableError(err)
}
}
return retry.NonRetryableError(err)
}
return nil
})
}

func credentialNeedsToBeUpdatedAfterCreation(plan awsCredentialResourceModel) bool {
return (!plan.SkipOrgPolicyDecisions.IsNull() && plan.SkipOrgPolicyDecisions.ValueBool()) || (!plan.VerifyPermissions.IsNull() || plan.VerifyPermissions.ValueBool())
}
68 changes: 68 additions & 0 deletions resources/environments/schema_aws_credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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 environments

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/resource"
"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/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
)

func (r *awsCredentialResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "The AWS credential is used for authorization to provision resources such as compute instances within your cloud provider account.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"credential_name": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"role_arn": schema.StringAttribute{
Required: true,
},
"description": schema.StringAttribute{
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplaceIfConfigured(),
},
},
"skip_org_policy_decisions": schema.BoolAttribute{
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false),
MarkdownDescription: "Whether to skip organizational policy decision checks or not.",
},
"verify_permissions": schema.BoolAttribute{
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false),
MarkdownDescription: "Whether to verify permissions upon saving or not.",
},
"crn": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
}
}
Loading