Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type-Based Semantic Equality #737

Merged
merged 2 commits into from May 31, 2023
Merged

Type-Based Semantic Equality #737

merged 2 commits into from May 31, 2023

Conversation

bflad
Copy link
Member

@bflad bflad commented Apr 28, 2023

Closes #70

This change set includes an initial implementation of type-based semantic equality functionality for the framework.

Semantic equality functionality enables provider developers to prevent drift detection and certain cases of Terraform data consistency errors, by writing logic that defines when a prior value should be preserved when compared to a new value with inconsequential differences. The definition of inconsequential depends on the context of the value, but typically it refers to when values have equivalent meaning with differing syntax. For example, the JSON specification defines whitespace as optional and object properties as unordered. Remote systems or code libraries may normalize JSON encoded strings into a differing byte ordering and/or length while remaining exactly equivalent in terms of the JSON specification.

Other example use cases:

  • Other encodings, such as Base64 or YAML string.
  • Networking values, such as an IPv4 address string.
  • Time values, such as an RFC3339 string.
  • Provider-specific values, such as an AWS ARN or AzureRM location string.

Type-based refers to the logic being baked into the extensible custom type system. This design is preferable over being schema-based, which refers to logic being repetitively coded across multiple attribute definitions. Being type-based means the provider developer can state these intentions by nature of pointing to a custom type which bakes in this logic, rather than needing to remember all the details to duplicate. For example, proper JSON string handling in the prior terraform-plugin-sdk required implementing an attribute as a string type with an appropriate JSON validation reference and JSON normalization reference. With these changes, a bespoke and reusable JSON string type with automatic validation and equivalency handling can be created.

Developers can implement semantic equality logic by implementing a new type-specific interface, which has one method that receives the new value and should return whether the prior value should be preserved or any diagnostics. An example implementation:

// Ensure the implementation satisfies the expected interfaces
// Other value type implementation details are omitted for brevity
var _ basetypes.StringValuableWithSemanticEquals = CustomStringValue{}

func (v CustomStringValue) 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.(CustomStringValue)

    if !ok {
        diags.AddError(
            "Semantic Equality Check Error",
            "An unexpected value type was received while performing semantic equality checks. "+
            "Please report this to the provider developers.\n\n"+
            "Expected Value Type: "+fmt.Sprintf("%T", v)+"\n"+
            "Got Value Type: "+fmt.Sprintf("%T", newValuable),
        )

        return
    }

    // Skipping error checking if CustomStringValue already implemented RFC3339 validation
    priorTime, _ := time.Parse(time.RFC3339, v.ValueString())

    // Skipping error checking if CustomStringValue already implemented RFC3339 validation
    newTime, _ := time.Parse(time.RFC3339, newValue.ValueString())

    // If the times are equivalent, keep the prior value
    return priorTime.Equal(newTime)
}

This value type functionality is checked in the following scenarios:

  • When refreshing a data source, the response state value from the Read method logic is compared to the configuration value.
  • When refreshing a resource, the response new state value from the Read method logic is compared to the request prior state value.
  • When creating or updating a resource, the response new state value from the Create or Update method logic is compared to the request plan value.

The framework will only call semantic equality logic if both the prior and new values are known. Null or unknown values are unnecessary to check.

The website documentation for custom types has been rewritten to remove details more pertinent to the original design of the framework and instead focus on how developers can either use existing custom types or create custom types based on the basetypes package Typable and Valuable interfaces.

@bflad bflad added the enhancement New feature or request label Apr 28, 2023
@bflad bflad added this to the v1.3.0 milestone Apr 28, 2023
Reference: #70

This change set includes an initial implemention of type-based semantic equality functionality for the framework.

Semantic equality functionality enables provider developers to prevent drift detection and certain cases of Terraform data consistency errors, by writing logic that defines when a prior value should be preserved when compared to a new value with inconsequential differences. The definition of inconsequential depends on the context of the value, but typically it refers to when a values have equivalent meaning with differing syntax. For example, the JSON specification defines whitespace as optional and object properties as unordered. Remote systems or code libraries may normalize JSON encoded strings into a differing byte ordering while remaining exactly equivalent in terms of the JSON specification.

Type-based refers to the logic being baked into the extensible custom type system. This design is preferable over being schema-based, which refers to logic being repetitively coded across multiple attribute definitions. Being type-based means the provider developer can state these intentions by nature of pointing to a custom type which bakes in this logic, rather than needing to remember all the details to duplicate. For example, proper JSON string handling in the prior terraform-plugin-sdk required implementing an attribute as a string type with an appropriate JSON validation reference and JSON normalization reference. With these changes, a bespoke and reusable JSON string type with automatic validation and equivalency handling can be created.

Developers can implement semantic equality logic by implementing a new type-specific interface, which has one method that receives the new value and should return whether the prior value should be preserved or any diagnostics.

The website documentation for custom types has been rewritten to remove details more pertinent to the original design of the framework and instead focus on how developers can either use existing custom types or create custom types based on the `basetypes` package `Typable` and `Valuable` interfaces.
@bflad bflad changed the title [WIP] Type-Based Semantic Equality Type-Based Semantic Equality May 26, 2023
@bflad bflad marked this pull request as ready for review May 26, 2023 19:09
@bflad bflad requested a review from a team as a code owner May 26, 2023 19:09
Copy link
Member

@austinvalle austinvalle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed out of band - one takeaway was small changes could be made to the migration guide to describe using custom types for StateFunc

🚀

Copy link
Contributor

@bendbennett bendbennett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🎸

@bflad bflad merged commit 7b8be49 into main May 31, 2023
19 checks passed
@bflad bflad deleted the bflad/semantic-equality branch May 31, 2023 21:00
@zliang-akamai
Copy link
Contributor

Glad to see this is implemented. Congrats!

@github-actions
Copy link

github-actions bot commented Jul 4, 2023

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Classifying normalization vs. drift
5 participants