Skip to content

Commit

Permalink
[CLOUDGA-24889] Show error during TF plan for immutable field change (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
bhupendray-yb authored Dec 18, 2024
1 parent 79634ed commit 17af3d7
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 10 deletions.
41 changes: 41 additions & 0 deletions managed/plan_modifier/plan_modifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package planmodifier

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)

// ImmutableFieldModifier is a plan modifier that enforces immutability of an attribute.
// This should be used with attribute's that can't be changed after resource creation and you
// want to show error to user during `tf plan` rather than `tf apply`.
//
// It should NOT be used for attribute's having "Computed: true" as they are already Read-Only.
//
// For nested attribute types, applying this plan modifier at root attribute is enough.
type ImmutableFieldModifier struct{}

func (m ImmutableFieldModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) {
if req.AttributeState == nil || req.AttributeState.IsNull() {
return
}

if !req.AttributeConfig.IsNull() && !req.AttributeConfig.Equal(req.AttributeState) {
resp.Diagnostics.AddError(
"Immutable Field Error",
fmt.Sprintf(
"Field '%s' is immutable and cannot be updated. Please destroy and recreate the resource if changes are needed.",
req.AttributePath.String(),
),
)
}
}

func (m ImmutableFieldModifier) Description(ctx context.Context) string {
return "Errors if the field is changed after resource creation"
}

func (m ImmutableFieldModifier) MarkdownDescription(ctx context.Context) string {
return "Errors if the field is changed after resource creation"
}
43 changes: 33 additions & 10 deletions managed/resource_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/yugabyte/terraform-provider-ybm/managed/fflags"
planmodifier "github.com/yugabyte/terraform-provider-ybm/managed/plan_modifier"
openapiclient "github.com/yugabyte/yugabytedb-managed-go-client-internal"
)

Expand Down Expand Up @@ -53,12 +54,18 @@ func (r resourceIntegrationType) getSchemaAttributes() map[string]tfsdk.Attribut
Description: "The name of the integration",
Type: types.StringType,
Required: true,
PlanModifiers: []tfsdk.AttributePlanModifier{
planmodifier.ImmutableFieldModifier{},
},
},
"type": {
Description: "Defines different exporter destination types.",
Type: types.StringType,
Required: true,
Validators: []tfsdk.AttributeValidator{stringvalidator.OneOf("DATADOG", "GRAFANA", "SUMOLOGIC", "GOOGLECLOUD", "PROMETHEUS", "VICTORIAMETRICS")},
PlanModifiers: []tfsdk.AttributePlanModifier{
planmodifier.ImmutableFieldModifier{},
},
},
"is_valid": {
Description: "Signifies whether the integration configuration is valid or not",
Expand All @@ -68,7 +75,10 @@ func (r resourceIntegrationType) getSchemaAttributes() map[string]tfsdk.Attribut
"datadog_spec": {
Description: "The specifications of a Datadog integration.",
Optional: true,
Validators: onlyContainsPath("datadog_spec"),
PlanModifiers: []tfsdk.AttributePlanModifier{
planmodifier.ImmutableFieldModifier{},
},
Validators: onlyContainsPath("datadog_spec"),
Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
"api_key": {
Description: "Datadog Api Key",
Expand All @@ -80,13 +90,15 @@ func (r resourceIntegrationType) getSchemaAttributes() map[string]tfsdk.Attribut
Description: "Datadog site.",
Type: types.StringType,
Required: true,
},
}),
}}),
},
"prometheus_spec": {
Description: "The specifications of a Prometheus integration.",
Optional: true,
Validators: onlyContainsPath("prometheus_spec"),
PlanModifiers: []tfsdk.AttributePlanModifier{
planmodifier.ImmutableFieldModifier{},
},
Validators: onlyContainsPath("prometheus_spec"),
Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
"endpoint": {
Description: "Prometheus OTLP endpoint URL e.g. http://my-prometheus-endpoint/api/v1/otlp",
Expand All @@ -98,7 +110,10 @@ func (r resourceIntegrationType) getSchemaAttributes() map[string]tfsdk.Attribut
"victoriametrics_spec": {
Description: "The specifications of a VictoriaMetrics integration.",
Optional: true,
Validators: onlyContainsPath("victoriametrics_spec"),
PlanModifiers: []tfsdk.AttributePlanModifier{
planmodifier.ImmutableFieldModifier{},
},
Validators: onlyContainsPath("victoriametrics_spec"),
Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
"endpoint": {
Description: "VictoriaMetrics OTLP endpoint URL e.g. http://my-victoria-metrics-endpoint/opentelemetry",
Expand All @@ -110,7 +125,10 @@ func (r resourceIntegrationType) getSchemaAttributes() map[string]tfsdk.Attribut
"grafana_spec": {
Description: "The specifications of a Grafana integration.",
Optional: true,
Validators: onlyContainsPath("grafana_spec"),
PlanModifiers: []tfsdk.AttributePlanModifier{
planmodifier.ImmutableFieldModifier{},
},
Validators: onlyContainsPath("grafana_spec"),
Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
"access_policy_token": {
Description: "Grafana Access Policy Token",
Expand Down Expand Up @@ -138,7 +156,10 @@ func (r resourceIntegrationType) getSchemaAttributes() map[string]tfsdk.Attribut
"sumologic_spec": {
Description: "The specifications of a Sumo Logic integration.",
Optional: true,
Validators: onlyContainsPath("sumologic_spec"),
PlanModifiers: []tfsdk.AttributePlanModifier{
planmodifier.ImmutableFieldModifier{},
},
Validators: onlyContainsPath("sumologic_spec"),
Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
"access_id": {
Description: "Sumo Logic Access Key ID",
Expand All @@ -151,8 +172,7 @@ func (r resourceIntegrationType) getSchemaAttributes() map[string]tfsdk.Attribut
Type: types.StringType,
Required: true,
Sensitive: true,
},
"installation_token": {
}, "installation_token": {
Description: "A Sumo Logic installation token to export telemetry to Grafana with",
Type: types.StringType,
Required: true,
Expand All @@ -163,7 +183,10 @@ func (r resourceIntegrationType) getSchemaAttributes() map[string]tfsdk.Attribut
"googlecloud_spec": {
Description: "The specifications of a Google Cloud integration.",
Optional: true,
Validators: onlyContainsPath("googlecloud_spec"),
PlanModifiers: []tfsdk.AttributePlanModifier{
planmodifier.ImmutableFieldModifier{},
},
Validators: onlyContainsPath("googlecloud_spec"),
Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
"type": {
Description: "Service Account Type",
Expand Down

0 comments on commit 17af3d7

Please sign in to comment.