diff --git a/stackit/internal/services/postgresflex/instance/resource.go b/stackit/internal/services/postgresflex/instance/resource.go index f20c9c73..c4ce7f56 100644 --- a/stackit/internal/services/postgresflex/instance/resource.go +++ b/stackit/internal/services/postgresflex/instance/resource.go @@ -131,7 +131,7 @@ func (r *instanceResource) Configure(ctx context.Context, req resource.Configure } // Schema defines the schema for the resource. -func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { descriptions := map[string]string{ "main": "Postgres Flex instance resource schema. Must have a `region` specified in the provider configuration.", "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`\".", @@ -199,13 +199,13 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r "id": schema.StringAttribute{ Computed: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), + UseStateForUnknownIfFlavorUnchanged(req), }, }, "description": schema.StringAttribute{ Computed: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), + UseStateForUnknownIfFlavorUnchanged(req), }, }, "cpu": schema.Int64Attribute{ diff --git a/stackit/internal/services/postgresflex/instance/use_state_for_unknown_if_flavor_unchanged_modifier.go b/stackit/internal/services/postgresflex/instance/use_state_for_unknown_if_flavor_unchanged_modifier.go new file mode 100644 index 00000000..38c924ba --- /dev/null +++ b/stackit/internal/services/postgresflex/instance/use_state_for_unknown_if_flavor_unchanged_modifier.go @@ -0,0 +1,85 @@ +package postgresflex + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +type useStateForUnknownIfFlavorUnchangedModifier struct { + Req resource.SchemaRequest +} + +// UseStateForUnknownIfFlavorUnchanged returns a plan modifier similar to UseStateForUnknown +// if the RAM and CPU values are not changed in the plan. Otherwise, the plan modifier does nothing. +func UseStateForUnknownIfFlavorUnchanged(req resource.SchemaRequest) planmodifier.String { + return useStateForUnknownIfFlavorUnchangedModifier{ + Req: req, + } +} + +func (m useStateForUnknownIfFlavorUnchangedModifier) Description(context.Context) string { + return "UseStateForUnknownIfFlavorUnchanged returns a plan modifier similar to UseStateForUnknown if the RAM and CPU values are not changed in the plan. Otherwise, the plan modifier does nothing." +} + +func (m useStateForUnknownIfFlavorUnchangedModifier) MarkdownDescription(ctx context.Context) string { + return m.Description(ctx) +} + +func (m useStateForUnknownIfFlavorUnchangedModifier) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) { // nolint:gocritic // function signature required by Terraform + // Do nothing if there is no state value. + if req.StateValue.IsNull() { + return + } + + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value, otherwise interpolation gets messed up. + if req.ConfigValue.IsUnknown() { + return + } + + // The above checks are taken from the UseStateForUnknown plan modifier implementation + // (https://github.com/hashicorp/terraform-plugin-framework/blob/main/resource/schema/stringplanmodifier/use_state_for_unknown.go#L38) + + var stateModel Model + diags := req.State.Get(ctx, &stateModel) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + var stateFlavor = &flavorModel{} + if !(stateModel.Flavor.IsNull() || stateModel.Flavor.IsUnknown()) { + diags = stateModel.Flavor.As(ctx, stateFlavor, basetypes.ObjectAsOptions{}) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + } + + var planModel Model + diags = req.Plan.Get(ctx, &planModel) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + var planFlavor = &flavorModel{} + if !(planModel.Flavor.IsNull() || planModel.Flavor.IsUnknown()) { + diags = planModel.Flavor.As(ctx, planFlavor, basetypes.ObjectAsOptions{}) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + } + + if planFlavor.CPU == stateFlavor.CPU && planFlavor.RAM == stateFlavor.RAM { + resp.PlanValue = req.StateValue + } +}