Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 59 additions & 6 deletions internal/fwserver/server_listresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ package fwserver

import (
"context"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"iter"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
"github.com/hashicorp/terraform-plugin-framework/internal/logging"
"github.com/hashicorp/terraform-plugin-framework/list"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// ListRequest is the framework server request for the ListResource RPC.
Expand Down Expand Up @@ -99,6 +100,47 @@ func (s *Server) ListResource(ctx context.Context, fwReq *ListRequest, fwStream
}
}

// TODO verdict is still out on how to handle diagnostics that pertain to the List call as a whole and not individual list results
diagsStream := &list.ListResultsStream{}

if listResourceWithConfigure, ok := listResource.(list.ListResourceWithConfigure); ok {
logging.FrameworkTrace(ctx, "ListResource implements ListResourceWithConfigure")

configureReq := resource.ConfigureRequest{
ProviderData: s.ListResourceConfigureData,
}

configureResp := resource.ConfigureResponse{}

logging.FrameworkTrace(ctx, "Called provider defined ListResource Configure")
listResourceWithConfigure.Configure(ctx, configureReq, &configureResp)
logging.FrameworkTrace(ctx, "Called provider defined ListResource Configure")

if len(configureResp.Diagnostics) > 0 {
diagsResp := list.ListResult{}

diagsResp.Diagnostics.Append(configureResp.Diagnostics...)

// Captures any diags from the Configure call
diagsStream.Results = func(push func(list.ListResult) bool) {
if !push(diagsResp) {
return
}
}

if diagsResp.Diagnostics.HasError() {
fwStream.Results = func(push func(ListResult) bool) {
for result := range diagsStream.Results {
if !push(ListResult(result)) {
return
}
}
}
return
}
}
}

req := list.ListRequest{
Config: *fwReq.Config,
IncludeResource: fwReq.IncludeResource,
Expand All @@ -118,14 +160,20 @@ func (s *Server) ListResource(ctx context.Context, fwReq *ListRequest, fwStream
stream.Results = list.NoListResults
}

fwStream.Results = processListResults(req, stream.Results)
if diagsStream.Results == nil {
diagsStream.Results = list.NoListResults
}

fwStream.Results = processListResults(req, stream.Results, diagsStream.Results)
Copy link
Member Author

Choose a reason for hiding this comment

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

@dbanck in order to capture any warning diagnostics that providers might set during the Configure call on line 115 above there might be cases where we need to append an empty ListResult with only a warning diagnostic in it to the stream.

How would this be handled by Terraform and would this be problematic?

Copy link
Member

Choose a reason for hiding this comment

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

In the current implementation we stop on errors, but continue on warnings.

But we also raise an error when a result has no identity.

https://github.com/hashicorp/terraform/blob/dec0edfd5afff7169f94424aae0590dddfddee8a/internal/plugin/grpc_provider.go#L1358-L1362

Copy link
Member

Choose a reason for hiding this comment

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

So appending a result with only a warning will still result in an error in Terraform, because the identity is missing

}

func processListResults(req list.ListRequest, stream iter.Seq[list.ListResult]) iter.Seq[ListResult] {
func processListResults(req list.ListRequest, streams ...iter.Seq[list.ListResult]) iter.Seq[ListResult] {
return func(push func(ListResult) bool) {
for result := range stream {
if !push(processListResult(req, result)) {
return
for _, stream := range streams {
for result := range stream {
if !push(processListResult(req, result)) {
return
}
}
}
}
Expand All @@ -138,6 +186,11 @@ func processListResult(req list.ListRequest, result list.ListResult) ListResult
return ListResult(result)
}

// Allow any non-error diags to pass through
if len(result.Diagnostics) > 0 && result.DisplayName == "" && result.Identity == nil && result.Resource == nil {
return ListResult(result)
}

if result.Identity == nil || result.Identity.Raw.IsNull() {
return ListResultError(
"Incomplete List Result",
Expand Down
34 changes: 0 additions & 34 deletions internal/proto5server/server_validatelistresourceconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,6 @@ func (s *Server) ValidateListResourceConfig(ctx context.Context, proto5Req *tfpr
return toproto5.ValidateListResourceConfigResponse(ctx, fwResp), nil
}

config, diags := fromproto5.Config(ctx, proto5Req.Config, listResourceSchema)

fwResp.Diagnostics.Append(diags...)

if diags.HasError() {
return toproto5.ValidateListResourceConfigResponse(ctx, fwResp), nil
}

resourceSchema, diags := s.FrameworkServer.ResourceSchema(ctx, proto5Req.TypeName)

fwResp.Diagnostics.Append(diags...)

if diags.HasError() {
return toproto5.ValidateListResourceConfigResponse(ctx, fwResp), nil
}

identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto5Req.TypeName)

fwResp.Diagnostics.Append(diags...)

if diags.HasError() {
return toproto5.ValidateListResourceConfigResponse(ctx, fwResp), nil
}

req := &fwserver.ListRequest{
Config: config,
ListResource: listResource,
ResourceSchema: resourceSchema,
ResourceIdentitySchema: identitySchema,
}
stream := &fwserver.ListResultsStream{}

s.FrameworkServer.ListResource(ctx, req, stream)

fwReq, diags := fromproto5.ValidateListResourceConfigRequest(ctx, proto5Req, listResource, listResourceSchema)

fwResp.Diagnostics.Append(diags...)
Expand Down
34 changes: 0 additions & 34 deletions internal/proto6server/server_validatelistresourceconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,6 @@ func (s *Server) ValidateListResourceConfig(ctx context.Context, proto6Req *tfpr
return toproto6.ValidateListResourceConfigResponse(ctx, fwResp), nil
}

config, diags := fromproto6.Config(ctx, proto6Req.Config, listResourceSchema)

fwResp.Diagnostics.Append(diags...)

if diags.HasError() {
return toproto6.ValidateListResourceConfigResponse(ctx, fwResp), nil
}

resourceSchema, diags := s.FrameworkServer.ResourceSchema(ctx, proto6Req.TypeName)

fwResp.Diagnostics.Append(diags...)

if diags.HasError() {
return toproto6.ValidateListResourceConfigResponse(ctx, fwResp), nil
}

identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto6Req.TypeName)

fwResp.Diagnostics.Append(diags...)

if diags.HasError() {
return toproto6.ValidateListResourceConfigResponse(ctx, fwResp), nil
}

req := &fwserver.ListRequest{
Config: config,
ListResource: listResource,
ResourceSchema: resourceSchema,
ResourceIdentitySchema: identitySchema,
}
stream := &fwserver.ListResultsStream{}

s.FrameworkServer.ListResource(ctx, req, stream)

fwReq, diags := fromproto6.ValidateListResourceConfigRequest(ctx, proto6Req, listResource, listResourceSchema)

fwResp.Diagnostics.Append(diags...)
Expand Down
Loading