Skip to content

Commit e42b2ec

Browse files
authored
Changes needed to support CMEK to TDE migration (#334)
1 parent 7b03faf commit e42b2ec

File tree

1 file changed

+53
-40
lines changed

1 file changed

+53
-40
lines changed

pkg/resource/service.go

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ func (r *ServiceResource) Schema(_ context.Context, _ resource.SchemaRequest, re
359359
"encryption_key": schema.StringAttribute{
360360
Description: "Custom encryption key ARN.",
361361
Optional: true,
362+
Computed: true,
362363
},
363364
"encryption_assumed_role_identifier": schema.StringAttribute{
364365
Description: "Custom role identifier ARN.",
@@ -372,19 +373,13 @@ func (r *ServiceResource) Schema(_ context.Context, _ resource.SchemaRequest, re
372373
"role_id": schema.StringAttribute{
373374
Computed: true,
374375
Description: "ID of Role to be used for granting access to the Encryption Key. This is an ARN for AWS services and a Service Account Identifier for GCP.",
375-
PlanModifiers: []planmodifier.String{
376-
stringplanmodifier.UseStateForUnknown(),
377-
},
378376
},
379377
"enabled": schema.BoolAttribute{
380378
Optional: true,
381379
Computed: true, // To allow client side defaulting.
382380
Description: "If true, TDE is enabled for the service.",
383381
},
384382
},
385-
PlanModifiers: []planmodifier.Object{
386-
objectplanmodifier.UseStateForUnknown(),
387-
},
388383
Validators: []validator.Object{
389384
objectvalidator.ConflictsWith(path.Expressions{path.MatchRoot("warehouse_id")}...),
390385
},
@@ -557,7 +552,7 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
557552
}
558553
}
559554

560-
if !plan.EncryptionKey.IsNull() && plan.EncryptionKey != state.EncryptionKey {
555+
if !config.EncryptionKey.IsNull() && plan.EncryptionKey != state.EncryptionKey {
561556
resp.Diagnostics.AddAttributeError(
562557
path.Root("encryption_key"),
563558
"Invalid Update",
@@ -597,7 +592,7 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
597592
resp.Diagnostics.AddAttributeError(
598593
path.Root("transparent_data_encryption.enabled"),
599594
"Invalid Update",
600-
"It is not possible to disable TDE (Transparent data encryption) on an existing service.",
595+
"This service has TDE enabled, but your clickhouse_service resource is not setting it as enabled. Please ensure you have set the transparent_data_encryption.enabled attribute to true",
601596
)
602597
}
603598

@@ -610,15 +605,6 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
610605
)
611606
}
612607

613-
if !wantEnabled {
614-
// Mark RoleID as known null.
615-
planTDE := models.TransparentEncryptionData{}
616-
plan.TransparentEncryptionData.As(ctx, &planTDE, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})
617-
planTDE.RoleID = types.StringNull()
618-
plan.TransparentEncryptionData = planTDE.ObjectValue()
619-
resp.Plan.Set(ctx, plan)
620-
}
621-
622608
if config.IdleTimeoutMinutes.IsNull() {
623609
plan.IdleTimeoutMinutes = types.Int64Null()
624610
resp.Plan.Set(ctx, plan)
@@ -666,7 +652,7 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
666652
)
667653
}
668654

669-
if !plan.EncryptionKey.IsNull() || !plan.EncryptionAssumedRoleIdentifier.IsNull() {
655+
if (!plan.EncryptionKey.IsNull() && !plan.EncryptionKey.IsUnknown()) || !plan.EncryptionAssumedRoleIdentifier.IsNull() {
670656
resp.Diagnostics.AddError(
671657
"Invalid Configuration",
672658
"encryption_key and encryption_assumed_role_identifier cannot be defined if the service tier is development",
@@ -718,17 +704,17 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
718704
)
719705
}
720706

721-
if !plan.EncryptionAssumedRoleIdentifier.IsNull() && plan.EncryptionKey.IsNull() {
707+
if !plan.EncryptionAssumedRoleIdentifier.IsNull() && (plan.EncryptionKey.IsNull() || plan.EncryptionKey.IsUnknown()) {
722708
resp.Diagnostics.AddError(
723709
"Invalid Configuration",
724710
"encryption_assumed_role_identifier cannot be defined without encryption_key as well",
725711
)
726712
}
727713

728-
if !plan.EncryptionKey.IsNull() && strings.Compare(plan.CloudProvider.ValueString(), "aws") != 0 {
714+
if !plan.EncryptionKey.IsNull() && !plan.EncryptionKey.IsUnknown() && strings.Compare(plan.CloudProvider.ValueString(), "aws") != 0 {
729715
resp.Diagnostics.AddError(
730716
"Invalid Configuration",
731-
"encryption_key and the encryption_assumed_role_identifier is only available for aws services",
717+
"encryption_key and the encryption_assumed_role_identifier is only available for AWS services",
732718
)
733719
}
734720

@@ -758,6 +744,18 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
758744
}
759745
}
760746
}
747+
748+
// CMEK->TDE migration.
749+
// if config.encryption_key is null, we need to wipe it out from the state even if the API returns it.
750+
// This happens when a service is migrated from CMEK to TDE.
751+
if config.EncryptionKey.IsNull() {
752+
if !state.EncryptionKey.IsNull() {
753+
plan.EncryptionKey = state.EncryptionKey
754+
resp.Plan.Set(ctx, plan)
755+
} else {
756+
plan.EncryptionKey = types.StringNull()
757+
}
758+
}
761759
}
762760

763761
if !plan.MinTotalMemoryGb.IsNull() && !plan.MinReplicaMemoryGb.IsUnknown() {
@@ -873,25 +871,31 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
873871
}
874872
}
875873

876-
defaultTDE := false
877874
{
878-
if config.TransparentEncryptionData.IsNull() || config.TransparentEncryptionData.IsUnknown() {
879-
defaultTDE = true
880-
} else {
881-
cfg := models.TransparentEncryptionData{}
882-
diag := config.TransparentEncryptionData.As(ctx, &cfg, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})
883-
if diag.HasError() {
884-
return
885-
}
886-
defaultTDE = cfg.Enabled.IsUnknown() || cfg.Enabled.IsNull()
875+
// Default value if there is no TDE attribute in the state nor in the config.
876+
tde := models.TransparentEncryptionData{
877+
Enabled: types.BoolValue(false),
887878
}
888-
if defaultTDE {
889-
plan.TransparentEncryptionData = models.TransparentEncryptionData{
890-
Enabled: types.BoolValue(false),
891-
RoleID: types.StringNull(),
892-
}.ObjectValue()
893-
resp.Plan.Set(ctx, plan)
879+
880+
// Read the TDE config if present to check if TDE is enabled or not.
881+
if !config.TransparentEncryptionData.IsNull() {
882+
configTDE := models.TransparentEncryptionData{}
883+
config.TransparentEncryptionData.As(ctx, &configTDE, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})
884+
885+
tde.Enabled = configTDE.Enabled
886+
tde.RoleID = types.StringUnknown()
887+
}
888+
889+
// Read the Role ID from the state if set
890+
if !req.State.Raw.IsNull() && !state.TransparentEncryptionData.IsNull() {
891+
stateTDE := models.TransparentEncryptionData{}
892+
state.TransparentEncryptionData.As(ctx, &stateTDE, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})
893+
894+
tde.RoleID = stateTDE.RoleID
894895
}
896+
897+
plan.TransparentEncryptionData = tde.ObjectValue()
898+
resp.Plan.Set(ctx, plan)
895899
}
896900

897901
if !config.DataWarehouseID.IsNull() {
@@ -964,7 +968,7 @@ func (r *ServiceResource) Create(ctx context.Context, req resource.CreateRequest
964968
service.MinReplicaMemoryGb = &minReplicaMemoryGb
965969
service.MaxReplicaMemoryGb = &maxReplicaMemoryGb
966970

967-
if !plan.EncryptionKey.IsNull() {
971+
if !plan.EncryptionKey.IsNull() && !plan.EncryptionKey.IsUnknown() {
968972
service.EncryptionKey = plan.EncryptionKey.ValueString()
969973
}
970974
if !plan.EncryptionAssumedRoleIdentifier.IsNull() {
@@ -1205,11 +1209,13 @@ func (r *ServiceResource) Read(ctx context.Context, req resource.ReadRequest, re
12051209
// Update updates the resource and sets the updated Terraform state on success.
12061210
func (r *ServiceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
12071211
// Retrieve values from plan
1208-
var plan, state models.ServiceResourceModel
1212+
var plan, state, config models.ServiceResourceModel
12091213
diags := req.Plan.Get(ctx, &plan)
12101214
resp.Diagnostics.Append(diags...)
12111215
diags = req.State.Get(ctx, &state)
12121216
resp.Diagnostics.Append(diags...)
1217+
req.Config.Get(ctx, &config)
1218+
resp.Diagnostics.Append(diags...)
12131219

12141220
if resp.Diagnostics.HasError() {
12151221
return
@@ -1539,6 +1545,13 @@ func (r *ServiceResource) Update(ctx context.Context, req resource.UpdateRequest
15391545
return
15401546
}
15411547

1548+
// CMEK->TDE migration.
1549+
// if config.encryption_key is null, we need to wipe it out from the state even if the API returns it.
1550+
// This happens when a service is migrated from CMEK to TDE.
1551+
if config.EncryptionKey.IsNull() {
1552+
plan.EncryptionKey = types.StringNull()
1553+
}
1554+
15421555
diags = resp.State.Set(ctx, plan)
15431556
resp.Diagnostics.Append(diags...)
15441557
if resp.Diagnostics.HasError() {
@@ -2009,7 +2022,7 @@ func (r *ServiceResource) syncServiceState(ctx context.Context, state *models.Se
20092022
Enabled: types.BoolValue(service.HasTransparentDataEncryption),
20102023
}
20112024

2012-
if service.HasTransparentDataEncryption && service.EncryptionRoleID != "" {
2025+
if service.EncryptionRoleID != "" {
20132026
tde.RoleID = types.StringValue(service.EncryptionRoleID)
20142027
} else {
20152028
tde.RoleID = types.StringNull()

0 commit comments

Comments
 (0)