hashicorp / hashicorp/terraform-plugin-framework

RequiresReplace does not consider SemanticEquals

Open
#1,168 1 comment 1 reaction 0 assignees View on GitHub
bug
Dominant language
Go
Stars
384
Forks
107
Avg merge
3m
Merged PRs (30d)
1

Description

Currently, the RequiresReplace planmodifier doesn't respect StringSemanticEquals.

...this may not be a bug, it may be working as intended. If so, would be nice to have a new plan modifier - something like `stringplanmodifier.RequiresReplaceIfSemanticallyDifferent()`

Thankyou!

### Module version

```
github.com/hashicorp/terraform-plugin-framework v1.15.0
```

### Relevant provider source code

I have defined a custom value `TrimmedStringValue` which is essentially identical to `StringValue`, but it ignores leading/trailing whitespace when semantic equality is checked.

```go
// trimmed_string_custom_type.go

var _ basetypes.StringValuable = TrimmedStringValue{}
var _ basetypes.StringValuableWithSemanticEquals = TrimmedStringValue{}

type TrimmedStringValue struct {
basetypes.StringValue
}

// ...

func (v TrimmedStringValue) StringSemanticEquals(ctx context.Context, newValuable basetypes.StringValuable) (bool, diag.Diagnostics) {
var diags diag.Diagnostics

// The framework should always pass the correct value type, but always check
newValue, ok := newValuable.(TrimmedStringValue)
if !ok {
diags.AddError("Semantic Equality Check Error", "...")
return false, diags
}

// ignore leading/training whitespace
priorString := strings.TrimSpace(v.StringValue.ValueString())
newString := strings.TrimSpace(newValue.ValueString())

// If the strings are equivalent, keep the prior value
return priorString == newString, diags
}
```

I then implement in in my schema with `CustomType: TrimmedStringType{}` and plan modifier `stringplanmodifier.RequiresReplace()`.

```go
// ssh_key_resource.go

func (r *sshKeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = resources.SshKeyResourceSchema(ctx)

// ...

publicKeyDescription := "The public key in OpenSSH \"authorized_keys\" format. This should be a valid public key, " +
"such as one generated by `ssh-keygen`."
resp.Schema.Attributes["public_key"] = schema.StringAttribute{
Required: true,
Description: publicKeyDescription,
MarkdownDescription: publicKeyDescription,
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
CustomType: TrimmedStringType{}, // Ignore leading/trailing whitespace
}
}
```

### Terraform Configuration Files

I then test this in my unit tests

First test step:

```hcl
resource "binarylane_ssh_key" "test" {
name = "tf-test-key-resource-test"
public_key = "` + publicKey + `"
}
```

Second test step:

```hcl
resource "binarylane_ssh_key" "test" {
name = "tf-test-key-resource-test"
# Test public key with a newline to ensure it does not get recreated
public_key = <

1. Implement custom type and value with `StringSemanticEquals`
2. Define schema with plan modifier `RequiresReplace`
3. Modify string while maintaining semantic equality

### References

- https://github.com/oscarhermoso/terraform-provider-binarylane/issues/76
- https://github.com/oscarhermoso/terraform-provider-binarylane/commit/6d559deddfe424e0944d648b945630202ad94d29

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.