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

Add resource schema-based support for static default values #674

Merged
merged 33 commits into from
Mar 20, 2023

Conversation

bendbennett
Copy link
Contributor

Closes: #668

@bendbennett bendbennett added the enhancement New feature or request label Feb 16, 2023
@bendbennett bendbennett added this to the v1.2.0 milestone Feb 16, 2023
Copy link
Member

@bflad bflad left a comment

Choose a reason for hiding this comment

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

Drive-by comments before we chat. 😄 Good stuff so far.

internal/fwschema/attribute_default.go Outdated Show resolved Hide resolved
tfsdk/state.go Outdated Show resolved Hide resolved
internal/fwserver/server_planresourcechange_test.go Outdated Show resolved Hide resolved
resource/schema/bool_attribute.go Outdated Show resolved Hide resolved
@bendbennett bendbennett marked this pull request as ready for review March 7, 2023 14:52
@bendbennett bendbennett requested a review from a team as a code owner March 7, 2023 14:52
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.

This looks awesome! 🎉

One general question, are we planning on adding a new section to the plugin framework docs about default values and the relevant interfaces? Similar to the plan modification docs

@bendbennett
Copy link
Contributor Author

One general question, are we planning on adding a new section to the plugin framework docs about default values and the relevant interfaces? Similar to the plan modification docs

Excellent question. Yep, we definitely should add to the docs. I'll attend to this. Thanks for flagging.

Copy link
Member

@bflad bflad left a comment

Choose a reason for hiding this comment

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

Overall I think this is pretty close! Some considerations and we'll likely want to figure out the best naming/strategy for the built-in default functions since those details would immediately be protected by compatibility promises.

// StaticValue returns a static number value default handler.
//
// Use StaticValue if a static default value for a number should be set.
func StaticValue(defaultVal types.Number) defaults.Number {
Copy link
Member

Choose a reason for hiding this comment

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

To prevent provider developer errors and keep this similar to the other primitive type functions, it may be good to use *big.Float here to represent a known value, instead of types.Number which could be set to null/unknown. We could consider different naming for these if we want to ensure there's room to support a types type variant -- e.g. calling this StaticBigFloat(), calling the int64 one StaticInt64(), etc, then leaving "StaticValue" for the types ones.

That said, we may want to consider adding error diagnostics from the default handling methods if the given default value is null or unknown. I'm not sure its valid to ever have either -- for null it should have Computed and Default removed, for unknown it could result permanent plan differences for practitioners.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've switched to using *big.Float and updated the primitives to use StaticBigFloat(), StaticInt() etc.

I've not added the error diagnostics for the handling methods if the default is null or unknown. Happy to do this, but I was wondering if we might want to discuss this first?

resource/schema/schema.go Outdated Show resolved Hide resolved
resource/schema/schema.go Outdated Show resolved Hide resolved
return diag.NewAttributeErrorDiagnostic(
path,
"Schema Using Attribute Default For Non-Computed Attribute",
fmt.Sprintf("attribute %q must be computed when using default", path.String()),
Copy link
Member

Choose a reason for hiding this comment

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

Nit: It'd be good to include our typical verbiage about "This is an issue with the provider and should be reported to the provider developers" in case a practitioner happens to receive it.

website/data/plugin-framework-nav-data.json Outdated Show resolved Hide resolved
// DefaultString runs the logic of the default. Access to the path is available in `req`, while
// `resp` contains fields for updating the planned value, and returning diagnostics.
func (m timeNowDefaultValue) DefaultString(_ context.Context, req defaults.StringRequest, resp *defaults.StringResponse)
resp.PlanValue = types.StringValue(time.Now().String())
Copy link
Member

Choose a reason for hiding this comment

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

How would you feel about showing a generic time.Time default similar to our "StaticValue" functions instead of hardcoding time.Now()?

Comment on lines 22 to 25
<Note>
Any Defaults that have been set are processed before any plan modifiers.
</Note>

Copy link
Member

Choose a reason for hiding this comment

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

Should this just be added to the list below instead?

// If the value is unknown or known, do not set default value.
if !req.PlanValue.IsNull() {
return
}

resp.PlanValue = types.StringValue(m.Default)
resp.PlanValue = types.StringValue(time.Now().String())
Copy link
Member

Choose a reason for hiding this comment

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

How do you feel about adjusting this to be a plan modifier that normalizes JSON-encoded strings (keeping the prior state if they match when normalized) so its separate from the default value use case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As discussed, have used one of the predefined UseStateForUnknown plan modifiers as an example.

defaultValue := a.BoolDefaultValue()
if defaultValue != nil {
resp := defaults.BoolResponse{}
defaultValue.DefaultBool(ctx, defaults.BoolRequest{}, &resp)
Copy link
Member

Choose a reason for hiding this comment

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

It'd be good to have trace/debug logging for each of these that we're calling provider-defined logic.

Copy link
Member

Choose a reason for hiding this comment

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

Perfect, thanks!

},
},
map[string]tftypes.Value{
"bool_attribute": tftypes.NewValue(tftypes.Bool, false), // value in state
Copy link
Member

Choose a reason for hiding this comment

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

Should the Description be updated to reflect this instead a code comment?

Copy link
Member

@bflad bflad left a comment

Choose a reason for hiding this comment

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

Excellent! Nice work. 🚀

website/docs/plugin/framework/resources/default.mdx Outdated Show resolved Hide resolved
"github.com/hashicorp/terraform-plugin-framework/types"
)

func TestStaticValueDefaultNumber(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Should this be TestStaticBigFloat to match the function name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've updated the naming of the /resource/schema static default test functions for primitives to make them consistent.

defaultValue := a.BoolDefaultValue()
if defaultValue != nil {
resp := defaults.BoolResponse{}
defaultValue.DefaultBool(ctx, defaults.BoolRequest{}, &resp)
Copy link
Member

Choose a reason for hiding this comment

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

Perfect, thanks!

@github-actions
Copy link

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 Apr 20, 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.

Resource schema-based support for static default values
3 participants