From d99fb700920958ddaaa7a3f7b85c43db32f9197d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9sz=C3=A1ros=20Gergely?= Date: Thu, 7 Nov 2024 17:47:31 +0100 Subject: [PATCH] PoC for env edit in case of AWS --- .../resource.tf | 96 +++++++++++++++++ .../environments/model_aws_environment.go | 2 + .../environments/resource_aws_environment.go | 70 +++++++++++- .../environments/schema_aws_environment.go | 8 ++ resources/environments/schema_freeipa.go | 1 + .../update_aws_environment_service.go | 101 ++++++++++++++++++ 6 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 resources/environments/update_aws_environment_service.go diff --git a/examples/resources/cdp_environments_aws_environment/resource.tf b/examples/resources/cdp_environments_aws_environment/resource.tf index cebad812..9bd4047d 100644 --- a/examples/resources/cdp_environments_aws_environment/resource.tf +++ b/examples/resources/cdp_environments_aws_environment/resource.tf @@ -38,3 +38,99 @@ output "environment_name" { output "crn" { value = cdp_environments_aws_environment.example.crn } + +output "status" { + value = cdp_environments_aws_environment.example.status +} + +output "status_reason" { + value = cdp_environments_aws_environment.example.status_reason +} + +output "cloud_platform" { + value = cdp_environments_aws_environment.example.region +} + +output "security_access" { + value = cdp_environments_aws_environment.example.security_access +} + +output "network_cidr" { + value = cdp_environments_aws_environment.example.network_cidr +} + +output "authentication" { + value = cdp_environments_aws_environment.example.authentication +} + +output "log_storage" { + value = cdp_environments_aws_environment.example.log_storage +} + +output "proxy_config_name" { + value = cdp_environments_aws_environment.example.proxy_config_name +} + +output "tags" { + value = cdp_environments_aws_environment.example.tags +} + +output "create_private_subnets" { + value = cdp_environments_aws_environment.example.create_private_subnets +} + +output "create_service_endpoints" { + value = cdp_environments_aws_environment.example.create_service_endpoints +} + +output "s3_guard_table_name" { + value = cdp_environments_aws_environment.example.s3_guard_table_name +} + +output "credential_name" { + value = cdp_environments_aws_environment.example.credential_name +} + +output "description" { + value = cdp_environments_aws_environment.example.description +} + +output "enable_tunnel" { + value = cdp_environments_aws_environment.example.enable_tunnel +} + +output "encryption_key_arn" { + value = cdp_environments_aws_environment.example.encryption_key_arn +} + +output "endpoint_access_gateway_scheme" { + value = cdp_environments_aws_environment.example.endpoint_access_gateway_scheme +} + +output "endpoint_access_gateway_subnet_ids" { + value = cdp_environments_aws_environment.example.endpoint_access_gateway_subnet_ids +} + +output "freeipa" { + value = cdp_environments_aws_environment.example.freeipa +} + +output "report_deployment_logs" { + value = cdp_environments_aws_environment.example.report_deployment_logs +} + +output "subnet_ids" { + value = cdp_environments_aws_environment.example.subnet_ids +} + +output "tunnel_type" { + value = cdp_environments_aws_environment.example.tunnel_type +} + +output "workload_analytics" { + value = cdp_environments_aws_environment.example.workload_analytics +} + +output "vpc_id" { + value = cdp_environments_aws_environment.example.vpc_id +} diff --git a/resources/environments/model_aws_environment.go b/resources/environments/model_aws_environment.go index 0f856c7f..4c60c9bd 100644 --- a/resources/environments/model_aws_environment.go +++ b/resources/environments/model_aws_environment.go @@ -100,4 +100,6 @@ type SecurityAccess struct { SecurityGroupIDForKnox types.String `tfsdk:"security_group_id_for_knox"` SecurityGroupIDsForKnox types.Set `tfsdk:"security_group_ids_for_knox"` + + GatewayNodeSecurityGroupID types.String `tfsdk:"gateway_node_security_group_id"` } diff --git a/resources/environments/resource_aws_environment.go b/resources/environments/resource_aws_environment.go index c2ef1775..c13ab678 100644 --- a/resources/environments/resource_aws_environment.go +++ b/resources/environments/resource_aws_environment.go @@ -12,6 +12,7 @@ package environments import ( "context" + "reflect" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -123,7 +124,65 @@ func (r *awsEnvironmentResource) Read(ctx context.Context, req resource.ReadRequ } func (r *awsEnvironmentResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan awsEnvironmentResourceModel + planDiags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(planDiags...) + if resp.Diagnostics.HasError() { + return + } + var state awsEnvironmentResourceModel + stateDiags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(stateDiags...) + if resp.Diagnostics.HasError() { + return + } + + if state.EncryptionKeyArn != plan.EncryptionKeyArn { + if err := updateAwsDiskEncryptionParameters(ctx, r.client.Environments, plan); err != nil { + utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "update disk encryption parameters") + return + } + state.EncryptionKeyArn = plan.EncryptionKeyArn + } + if plan.Authentication != nil && (plan.Authentication != state.Authentication) { + if err := updateSshKey(ctx, r.client.Environments, plan.Authentication, plan.EnvironmentName.ValueStringPointer()); err != nil { + utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "update SSH key") + return + } + state.Authentication = plan.Authentication + } + if !reflect.DeepEqual(utils.FromSetValueToStringList(plan.SubnetIds), utils.FromSetValueToStringList(state.SubnetIds)) || + !reflect.DeepEqual(plan.EndpointAccessGatewaySubnetIds, state.EndpointAccessGatewaySubnetIds) { + if err := updateSubnet(ctx, r.client.Environments, plan); err != nil { + utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "update subnet") + return + } + state.SubnetIds = plan.SubnetIds + state.EndpointAccessGatewaySubnetIds = plan.EndpointAccessGatewaySubnetIds + } + if plan.SecurityAccess != nil && (plan.SecurityAccess != state.SecurityAccess) { + if err := updateSecurityAccess(ctx, r.client.Environments, plan); err != nil { + utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "update security access") + return + } + state.SecurityAccess = plan.SecurityAccess + } + if !plan.Tags.IsNull() && !reflect.DeepEqual(plan.Tags, state.Tags) { + if err := updateTags(ctx, r.client.Environments, plan); err != nil { + utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "update tags") + return + } + state.Tags = plan.Tags + } + if plan.ProxyConfigName != state.ProxyConfigName { + if err := updateProxyConfig(ctx, r.client.Environments, plan); err != nil { + utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "update proxy config") + return + } + state.ProxyConfigName = plan.ProxyConfigName + } + resp.State.Set(ctx, state) } func (r *awsEnvironmentResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { @@ -211,11 +270,12 @@ func toAwsEnvironmentResource(ctx context.Context, env *environmentsmodels.Envir sgIDsknox = model.SecurityAccess.SecurityGroupIDsForKnox } model.SecurityAccess = &SecurityAccess{ - Cidr: types.StringValue(env.SecurityAccess.Cidr), - DefaultSecurityGroupID: types.StringValue(env.SecurityAccess.DefaultSecurityGroupID), - DefaultSecurityGroupIDs: dsgIDs, - SecurityGroupIDForKnox: types.StringValue(env.SecurityAccess.SecurityGroupIDForKnox), - SecurityGroupIDsForKnox: sgIDsknox, + Cidr: types.StringValue(env.SecurityAccess.Cidr), + DefaultSecurityGroupID: types.StringValue(env.SecurityAccess.DefaultSecurityGroupID), + DefaultSecurityGroupIDs: dsgIDs, + SecurityGroupIDForKnox: types.StringValue(env.SecurityAccess.SecurityGroupIDForKnox), + SecurityGroupIDsForKnox: sgIDsknox, + GatewayNodeSecurityGroupID: model.SecurityAccess.GatewayNodeSecurityGroupID, } } model.Status = types.StringPointerValue(env.Status) diff --git a/resources/environments/schema_aws_environment.go b/resources/environments/schema_aws_environment.go index b01bcf49..6d1db788 100644 --- a/resources/environments/schema_aws_environment.go +++ b/resources/environments/schema_aws_environment.go @@ -12,6 +12,7 @@ package environments import ( "context" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" @@ -37,6 +38,7 @@ var AwsEnvironmentSchema = schema.Schema{ }, }, "crn": schema.StringAttribute{ + Optional: true, Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), @@ -224,6 +226,9 @@ var AwsEnvironmentSchema = schema.Schema{ Optional: true, ElementType: types.StringType, }, + "gateway_node_security_group_id": schema.StringAttribute{ + Optional: true, + }, }, }, "status": schema.StringAttribute{ @@ -242,6 +247,9 @@ var AwsEnvironmentSchema = schema.Schema{ Optional: true, Computed: true, ElementType: types.StringType, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, }, "tags": schema.MapAttribute{ Optional: true, diff --git a/resources/environments/schema_freeipa.go b/resources/environments/schema_freeipa.go index 7c6d6689..82ee6698 100644 --- a/resources/environments/schema_freeipa.go +++ b/resources/environments/schema_freeipa.go @@ -60,6 +60,7 @@ var FreeIpaSchema = schema.SingleNestedAttribute{ }, "instances": schema.SetNestedAttribute{ Computed: true, + Optional: true, PlanModifiers: []planmodifier.Set{ setplanmodifier.UseStateForUnknown(), }, diff --git a/resources/environments/update_aws_environment_service.go b/resources/environments/update_aws_environment_service.go new file mode 100644 index 00000000..e6c0df85 --- /dev/null +++ b/resources/environments/update_aws_environment_service.go @@ -0,0 +1,101 @@ +// 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-log/tflog" + + 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" +) + +func updateAwsDiskEncryptionParameters(ctx context.Context, client *environmentsclient.Environments, plan awsEnvironmentResourceModel) error { + params := operations.NewUpdateAwsDiskEncryptionParametersParamsWithContext(ctx) + params.WithInput(&environmentsmodels.UpdateAwsDiskEncryptionParametersRequest{ + EncryptionKeyArn: plan.EncryptionKeyArn.ValueStringPointer(), + Environment: plan.EnvironmentName.ValueStringPointer(), + }) + tflog.Info(ctx, "Updating disk encryption parameters in the environment") + _, err := client.Operations.UpdateAwsDiskEncryptionParameters(params) + return err +} + +func updateSshKey(ctx context.Context, client *environmentsclient.Environments, authPlan *Authentication, env *string) error { + params := operations.NewUpdateSSHKeyParamsWithContext(ctx) + if !authPlan.PublicKey.IsNull() && authPlan.PublicKey.ValueString() != "" { + params.WithInput(&environmentsmodels.UpdateSSHKeyRequest{ + Environment: env, + NewPublicKey: authPlan.PublicKey.ValueString(), + ExistingPublicKeyID: "", + }) + } else { + params.WithInput(&environmentsmodels.UpdateSSHKeyRequest{ + Environment: env, + ExistingPublicKeyID: authPlan.PublicKeyID.ValueString(), + NewPublicKey: "", + }) + } + tflog.Info(ctx, "Updating SSH key in the environment") + _, err := client.Operations.UpdateSSHKey(params) + return err +} + +func updateSubnet(ctx context.Context, client *environmentsclient.Environments, plan awsEnvironmentResourceModel) error { + params := operations.NewUpdateSubnetParamsWithContext(ctx) + params.WithInput(&environmentsmodels.UpdateSubnetRequest{ + Environment: plan.EnvironmentName.ValueStringPointer(), + SubnetIds: utils.FromSetValueToStringList(plan.SubnetIds), + EndpointAccessGatewaySubnetIds: utils.FromSetValueToStringList(plan.EndpointAccessGatewaySubnetIds), + }) + tflog.Info(ctx, "Updating subnet(s) in the environment") + _, err := client.Operations.UpdateSubnet(params) + return err +} + +func updateSecurityAccess(ctx context.Context, client *environmentsclient.Environments, plan awsEnvironmentResourceModel) error { + params := operations.NewUpdateSecurityAccessParamsWithContext(ctx) + params.WithInput(&environmentsmodels.UpdateSecurityAccessRequest{ + DefaultSecurityGroupID: plan.SecurityAccess.DefaultSecurityGroupID.ValueStringPointer(), + Environment: plan.EnvironmentName.ValueStringPointer(), + GatewayNodeSecurityGroupID: plan.SecurityAccess.GatewayNodeSecurityGroupID.ValueStringPointer(), + }) + tflog.Info(ctx, "Updating security access in the environment") + _, err := client.Operations.UpdateSecurityAccess(params) + return err +} + +func updateTags(ctx context.Context, _ *environmentsclient.Environments, _ awsEnvironmentResourceModel) error { + tflog.Error(ctx, "UpdateTags is not implemented yet, it has to be present in BETA SDK beforehand") + return nil +} + +func updateProxyConfig(ctx context.Context, client *environmentsclient.Environments, plan awsEnvironmentResourceModel) error { + params := operations.NewUpdateProxyConfigParamsWithContext(ctx) + if plan.ProxyConfigName.IsNull() || plan.ProxyConfigName.ValueString() == "" { + params.WithInput(&environmentsmodels.UpdateProxyConfigRequest{ + Environment: plan.EnvironmentName.ValueStringPointer(), + RemoveProxy: true, + }) + tflog.Info(ctx, "Removing proxy configuration from the environment") + } else { + params.WithInput(&environmentsmodels.UpdateProxyConfigRequest{ + Environment: plan.EnvironmentName.ValueStringPointer(), + ProxyConfigName: plan.ProxyConfigName.ValueString(), + }) + tflog.Info(ctx, "Updating proxy configuration in the environment") + } + _, err := client.Operations.UpdateProxyConfig(params) + return err +}