Skip to content

Add ListResource RPC #1157

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

Merged
merged 13 commits into from
Jun 18, 2025
Merged

Add ListResource RPC #1157

merged 13 commits into from
Jun 18, 2025

Conversation

bbasata
Copy link
Contributor

@bbasata bbasata commented Jun 5, 2025

Description

This change adds the ListResource RPC.

This is ~80% ready, so I feel okay moving it out of draft mode. I plan to make a round of code review comments, as a guided walkthrough.

Rollback Plan

  • If a change needs to be reverted, we will roll out an update to the code within 7 days.

Changes to Security Controls

None

@bbasata bbasata mentioned this pull request Jun 5, 2025
1 task
@bbasata bbasata changed the base branch from main to add-list-to-fwserver-part-ii June 5, 2025 14:17
@bbasata bbasata added this to the v1.16.0 milestone Jun 5, 2025
Base automatically changed from add-list-to-fwserver-part-ii to main June 5, 2025 14:53
@bbasata bbasata force-pushed the add-listresource-rpc branch from 6414d9d to ed02781 Compare June 13, 2025 18:56
@bbasata bbasata changed the title Add listresource rpc Add ListResource RPC Jun 13, 2025
@bbasata bbasata force-pushed the add-listresource-rpc branch 2 times, most recently from 56424fa to d5d9cb5 Compare June 13, 2025 19:55
@bbasata bbasata changed the base branch from main to revisit-list-package June 13, 2025 19:59
@bbasata bbasata force-pushed the add-listresource-rpc branch 2 times, most recently from af2d8f7 to 380b706 Compare June 13, 2025 20:05
@bbasata bbasata marked this pull request as ready for review June 13, 2025 20:07
@bbasata bbasata requested a review from a team as a code owner June 13, 2025 20:07
@bbasata bbasata force-pushed the add-listresource-rpc branch from 380b706 to ce0467f Compare June 13, 2025 20:22
@@ -68,7 +80,8 @@ func (s *Server) ListResourceFuncs(ctx context.Context) (map[string]func() list.
continue
}

if _, ok := s.resourceFuncs[typeName]; !ok {
resourceFuncs, _ := s.ResourceFuncs(ctx)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cross-ref: #1152 (comment)

This feels more robust. And removes the need for "call X before Y" comments.

Results iter.Seq[ListResult]
}

func ListResultError(summary string, detail string) ListResult {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO: add a doc comment

Copy link
Member

Choose a reason for hiding this comment

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

Since it's not a "publicly" exported field, no need to add a doc comment unless you feel this helper needs more explanation for maintainers.

Although I'm wondering if this function even needs to be exported (or even exist, it's only used once ATM, maybe it will be used more later?)

Base automatically changed from revisit-list-package to main June 16, 2025 14:38
@@ -69,7 +85,8 @@ func (s *Server) ListResourceFuncs(ctx context.Context) (map[string]func() list.
continue
}

if _, ok := s.resourceFuncs[typeName]; !ok {
resourceFuncs, _ := s.ResourceFuncs(ctx)
if _, ok := resourceFuncs[typeName]; !ok {
s.listResourceFuncsDiags.AddError(
"ListResource Type Defined without a Matching Managed Resource Type",
fmt.Sprintf("The %s ListResource type name was returned, but no matching managed Resource type was defined. ", typeName)+
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO, after this PR: we want the flexibility for a list resource in Framework to work with a managed resource type in SDKv2. So we'll revisit this logic.

return ListRequestErrorDiagnostics(ctx, allDiags...)
}

resourceSchema, diags := s.FrameworkServer.ResourceSchema(ctx, protoReq.TypeName)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO, after this PR: we want the flexibility for a list resource in Framework to work with a managed resource type in SDKv2. So we'll revisit this logic.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I'd think since fwserver doesn't need these values, then this would just be something could be a nil all the way through to the List call. Where the provider developer would then just construct tfsdk.Resource themselves? (which needs to happen anyways?)

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 think this is the fun part. As in #1157 (comment), good enough for this PR while under construction for SDKv2 considerations.

As I piece this together, I see that a provider developer typically writes:

resp.State.Set(ctx, &data)

and this works because fwserver has pre-set resp.State.Schema:

readResp := resource.ReadResponse{
State: tfsdk.State{
Schema: req.CurrentState.Schema,
Raw: req.CurrentState.Raw.Copy(),
},
}

This is the one reason that fwserver.ListResource is interested in the resource schema and resource identity schema.

And then the "pre-set" mechanics are a little different from resp.State because List returns multiple entities instead of a single entity, as you've referred to in #1157 (comment). We definitely have room to improve on that.

In the next form of this, I'm hoping we can unexport the *Schema struct fields ✅

return ListRequestErrorDiagnostics(ctx, allDiags...)
}

identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, protoReq.TypeName)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO, after this PR: we want the flexibility for a list resource in Framework to work with a managed resource type in SDKv2. So we'll revisit this logic.

return identity, diags
}

func (r ListRequest) ToListResult(ctx context.Context, identityVal any, resourceVal any, displayName string) ListResult {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO, after this PR: we want the flexibility for a list resource in Framework to work with a managed resource type in SDKv2. So we'll revisit this helper method.

austinvalle
austinvalle previously approved these changes Jun 17, 2025
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.

Overall this LGTM, I left some general comments and thoughts

Comment on lines 84 to 86
if fwReq.Config == nil {
return errors.New("Invalid ListResource request: Config cannot be nil")
}
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason we aren't using response diagnostics here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's no functional reason. I was on the fence re: error or diagnostics. I learned that Terraform can work with either one, so error struck me as simpler for a request-level nil check.

Now that we have a 👍 on "push a single event with an error diagnostic," we could totally use diagnostics instead. I'm open to that. Any preference?

Copy link
Member

Choose a reason for hiding this comment

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

Most of framework (like the type system for example) uses diagnostics, which are just a summary/detail, so I'd say we should stick to diagnostics unless we can't for whatever reason 👍🏻

return ListResult(result)
}

if result.Identity == nil { // TODO: is result.Identity.Raw.IsNull() a practical concern?
Copy link
Member

Choose a reason for hiding this comment

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

I guess the question would: does Terraform core care if the provider returns a null identity? I would say we probably shouldn't return a null identity unless core says that's acceptable.

In which case we should just check both:

  • result.Identity == nil being the provider likely didn't set anything
  • result.Identity.Raw.IsNull() being the provider explicitly set a null value

Copy link
Contributor Author

Choose a reason for hiding this comment

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

does Terraform core care if the provider returns a null identity? I would say we probably shouldn't return a null identity unless core says that's acceptable.

cc: @dbanck @dsa0x for input

Copy link
Member

Choose a reason for hiding this comment

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

Terraform currently raises an error when a list result has no identity

Copy link
Member

Choose a reason for hiding this comment

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

This is probably the area we'd want the most feedback on from provider developers right? One thought might be to store the identity/resource schemas on ListResultsStream rather then on the request (also do they need to be exported? 🤔 )

Would a variant of this for multiple list results also we worth doing?

Copy link
Member

Choose a reason for hiding this comment

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

For example, today we have a lot of resp.Set which internally use schema data, maybe this would end up being a result.Append variation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bbasata bbasata enabled auto-merge (squash) June 18, 2025 15:30
@bbasata
Copy link
Contributor Author

bbasata commented Jun 18, 2025

Ready for re-review and auto-merge.

TODO items for follow-up:

Perhaps future PR, but if this is a warning that practitioners are likely to see often, maybe we should tailor it specifically to them (i.e. not using the Go types since they have no reference of what these fields mean and won't be reporting to provider developers)

// TODO: is result.Resource.Raw.IsNull() a practical concern? / does Terraform core care if the provider returns a null identity? I would say we probably shouldn't return a null identity unless core says that's acceptable.

Now that we have a 👍 on "push a single event with an error diagnostic," we could totally use diagnostics instead. I'm open to that. Any preference?

Interesting... I'm not really sure what to do about this. We've never just returned the schema in the request like this before, they are typically used to pre-populate a response object and in the case of them potentially not existing (like during upgrade), we just don't populate the response object.

Here, the results stream won't be pre-populated, but provider developers are required to pass along the same schema/identity schema with each result field. Maybe that implementation detail can be placed somewhere in framework?

Yes, I consider this good-enough for this PR, while still under construction. When we look at handling non-Framework schema types, I think that adds new design information that will be useful.

@bbasata bbasata requested a review from austinvalle June 18, 2025 15:31
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.

LGTM 🚀

@bbasata bbasata merged commit 9cafd72 into main Jun 18, 2025
35 checks passed
@bbasata bbasata deleted the add-listresource-rpc branch June 18, 2025 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants