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

Skip validation of a values missing in config #259

Closed
wants to merge 4 commits into from

Conversation

gzigzigzeo
Copy link
Contributor

@gzigzigzeo gzigzigzeo commented Feb 7, 2022

Problem

I have the following schema:

"spec": {
	Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
		"options": {
			Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
				"lock": {
					Description: "Lock specifies the locking mode (strict|best_effort) to be applied with  the role.",
					Optional:    true,
					Type:        types.StringType,
				},
				"max_sessions": {
					Description: "MaxSessions defines the maximum number of  concurrent sessions per connection.",
					Optional:    true,
					Computed:    true,
					Type:        types.Int64Type,
				},
			}),
			Computed:    true,
			Description: "Options is for OpenSSH options like agent forwarding.",
			Optional:    true,
			PlanModifiers: []tfsdk.AttributePlanModifier{tfsdk.UseStateForUnknown()},
		},

And the following config:

spec {}

"spec" is the base object, "options" is the child computed object, and "max_sessions" is the bottommost Int64 value, which, in turn, is also computed.

options might either be missing in my configuration at all, set partially or set completely, including max_sessions attribute.

When options section is missing in the configuration completely, Terraform crashes:

github.com/hashicorp/terraform-plugin-framework/types.int64Validate({0x103337498, 0x1400030b600}, {{0x0, 0x0}, {0x0, 0x0}}, 0x14000683ce0)
	/Users/gzigzigzeo/go/src/github.com/gravitational/teleport-plugins/vendor/github.com/hashicorp/terraform-plugin-framework/types/int64.go:16 +0x44
github.com/hashicorp/terraform-plugin-framework/types.primitive.Validate(0x3, {0x103337498, 0x1400030b600}, {{0x0, 0x0}, {0x0, 0x0}}, 0x14000683ce0)
	/Users/gzigzigzeo/go/src/github.com/gravitational/teleport-plugins/vendor/github.com/hashicorp/terraform-plugin-framework/types/primitive.go:121 +0xd8
github.com/hashicorp/terraform-plugin-framework/tfsdk.Config.getAttributeValue({{{0x103341820, 0x140004f8690}, {0x1031eb6a0, 0x140004f41e0}}, {0x14000558bd0, 0x0, 0x0, {0x0, 0x0}, {0x0, ...}, ...}}, ...)

Solution

That happens because Terraform Framework tries to validate the type of spec.options.max_sessions config value, which is missing in the config, hence, can't have a type to validate. It must be skipped.

@gzigzigzeo gzigzigzeo requested a review from a team as a code owner February 7, 2022 22:18
@hashicorp-cla
Copy link

hashicorp-cla commented Feb 7, 2022

CLA assistant check
All committers have signed the CLA.

@ewbankkit
Copy link
Contributor

Similar crash reported in hashicorp/terraform-provider-awscc#382.
In that case the offending code is

// Ignoring ErrInvalidStep will allow this method to return a null value of the type.
if err != nil && !errors.Is(err, tftypes.ErrInvalidStep) {
diags.AddAttributeError(
path,
"State Read Error",
"An unexpected error was encountered trying to read an attribute from the state. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
)
return nil, diags
}
// TODO: If ErrInvalidStep, check parent paths for unknown value.
// If found, convert this value to an unknown value.
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/186
if attrTypeWithValidate, ok := attrType.(attr.TypeWithValidate); ok {
diags.Append(attrTypeWithValidate.Validate(ctx, tfValue, path)...)
if diags.HasError() {
return nil, diags
}
}

@gzigzigzeo
Copy link
Contributor Author

@ewbankkit Thanks for pointing this out, provided failed test and fixed it as well.

@bflad bflad self-assigned this Feb 22, 2022
@bflad bflad added the bug Something isn't working label Feb 22, 2022
@bflad
Copy link
Contributor

bflad commented Feb 22, 2022

Hi @gzigzigzeo 👋 Thanks for submitting this and sorry you ran into trouble here.

In general, the preference for this framework should be performing attribute validation in all cases. This allows provider developers to check against null values explicitly, rather than the framework making any assumptions about what might be the correct behavior. To that end, this likely means that some of the built-in framework types need to handle this class of nil tftypes.Value issue to prevent panics.

My recommendation here would be to revert the general validation logic and instead perform a nil check before the problematic call into in.Type(), e.g.

if in == nil {
  return diags
}

if !in.Type().Equal(tftypes.Number) { // ...

Please reach out if you have any questions (although it may take a week for a next response).

@gzigzigzeo
Copy link
Contributor Author

Hey, @bflad! Thank you for the clarification! In fact, reverting of this logic was my initial thought, and performing it in float64.go and int64.go fixed the issue. At that moment, I thought that it was a bit implicit and does not guarantee a custom user validations from failing. I now see why your approach is better and framework should not guarantee anything here. I am going to fix my implementation accordingly, add a counterpart tests for other built-in types and push it to this PR. Thanks again.

@gzigzigzeo
Copy link
Contributor Author

@bflad Done!

Copy link
Contributor

@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.

Thanks for the updates, @gzigzigzeo. Since I didn't want to break your main branch by updating it, this will get pulled in as a separate commit. I will also fix the Set validation (#261) as part of pulling this in.

Comment on lines +338 to +359
"Computed-Computed-object": {
config: Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"name": tftypes.String,
},
}, map[string]tftypes.Value{
"name": tftypes.NewValue(tftypes.String, "namevalue"),
}),
Schema: Schema{
Attributes: map[string]Attribute{
"name": {
Type: testtypes.StringTypeWithValidateWarning{},
Required: true,
},
},
},
},
target: new(testtypes.String),
expected: &testtypes.String{String: types.String{Value: "namevalue"}, CreatedBy: testtypes.StringTypeWithValidateWarning{}},
expectedDiags: diag.Diagnostics{testtypes.TestWarningDiagnostic(tftypes.NewAttributePath().WithAttributeName("name"))},
},
Copy link
Contributor

Choose a reason for hiding this comment

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

This test looks identical to the one above, so will remove on merge.

@@ -91,11 +91,13 @@ func (s State) getAttributeValue(ctx context.Context, path *tftypes.AttributePat
// If found, convert this value to an unknown value.
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/186

if attrTypeWithValidate, ok := attrType.(attr.TypeWithValidate); ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to Config, this logic should not be changed, will fix on merge.

@@ -1585,6 +1607,35 @@ func TestConfigGetAttributeValue(t *testing.T) {
expected: testtypes.String{String: types.String{Value: "value"}, CreatedBy: testtypes.StringTypeWithValidateWarning{}},
expectedDiags: diag.Diagnostics{testtypes.TestWarningDiagnostic(tftypes.NewAttributePath().WithAttributeName("test"))},
},
"AttrTypeInt64WithValidateError-nested-missing-in-config": {
Copy link
Contributor

Choose a reason for hiding this comment

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

Will ensure there is a covering test in plan as well.

@bflad
Copy link
Contributor

bflad commented Mar 11, 2022

Merged as c584343

@bflad bflad closed this Mar 11, 2022
marcoandredinis added a commit to gravitational/teleport-plugins that referenced this pull request Apr 4, 2022
This fork was created because of this PR
hashicorp/terraform-plugin-framework#259

It was upstreamed as part of the 0.6.1 release:
https://github.com/hashicorp/terraform-plugin-framework/releases/tag/v0.6.1

We can now remove the replace directive
@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 11, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants