Skip to content

Commit 685ff9f

Browse files
authored
Schema representation of list block config and results (#37209)
1 parent b3d7dae commit 685ff9f

27 files changed

+1535
-278
lines changed

internal/addrs/parse_target.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ func parseResourceInstanceUnderModule(moduleAddr ModuleInstance, allowPartial bo
177177
case "ephemeral":
178178
mode = EphemeralResourceMode
179179
remain = remain[1:]
180+
case "list":
181+
mode = ListResourceMode
182+
remain = remain[1:]
180183
case "resource":
181184
// Starting a resource address with "resource" is optional, so we'll
182185
// just ignore it.

internal/configs/configschema/internal_validate.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ func (a *Attribute) internalValidate(name, prefix string) error {
175175
func (o *Object) InternalValidate() error {
176176
var err error
177177

178+
if o.Nesting == nestingModeInvalid {
179+
return fmt.Errorf("object schema nesting mode is invalid")
180+
}
181+
178182
for name, attrS := range o.Attributes {
179183
if attrS == nil {
180184
err = errors.Join(err, fmt.Errorf("%s: attribute schema is nil", name))

internal/configs/configschema/internal_validate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ func TestObjectInternalValidate(t *testing.T) {
333333
Errs []string
334334
}{
335335
"empty": {
336-
&Object{},
336+
&Object{Nesting: NestingSingle},
337337
[]string{},
338338
},
339339
"valid": {

internal/plans/changes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ func (c *Changes) Encode(schemas *schemarepo.Schemas) (*ChangesSrc, error) {
5656
schema = p.ResourceTypes[rc.Addr.Resource.Resource.Type]
5757
case addrs.DataResourceMode:
5858
schema = p.DataSources[rc.Addr.Resource.Resource.Type]
59+
case addrs.ListResourceMode:
60+
schema = p.ListResourceTypes[rc.Addr.Resource.Resource.Type]
5961
default:
6062
panic(fmt.Sprintf("unexpected resource mode %s", rc.Addr.Resource.Resource.Mode))
6163
}

internal/plans/changes_src.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ func (c *ChangesSrc) Decode(schemas *schemarepo.Schemas) (*Changes, error) {
114114
schema = p.ResourceTypes[rcs.Addr.Resource.Resource.Type]
115115
case addrs.DataResourceMode:
116116
schema = p.DataSources[rcs.Addr.Resource.Resource.Type]
117+
case addrs.ListResourceMode:
118+
schema = p.ListResourceTypes[rcs.Addr.Resource.Resource.Type]
117119
default:
118120
panic(fmt.Sprintf("unexpected resource mode %s", rcs.Addr.Resource.Resource.Mode))
119121
}

internal/plugin/grpc_provider.go

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"google.golang.org/grpc/status"
2222

2323
"github.com/hashicorp/terraform/internal/addrs"
24+
"github.com/hashicorp/terraform/internal/configs/configschema"
2425
"github.com/hashicorp/terraform/internal/logging"
2526
"github.com/hashicorp/terraform/internal/plugin/convert"
2627
"github.com/hashicorp/terraform/internal/providers"
@@ -170,7 +171,24 @@ func (p *GRPCProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
170171
}
171172

172173
for name, list := range protoResp.ListResourceSchemas {
173-
resp.ListResourceTypes[name] = convert.ProtoToProviderSchema(list, nil)
174+
ret := convert.ProtoToProviderSchema(list, nil)
175+
resp.ListResourceTypes[name] = providers.Schema{
176+
Version: ret.Version,
177+
Body: &configschema.Block{
178+
Attributes: map[string]*configschema.Attribute{
179+
"data": {
180+
Type: cty.DynamicPseudoType,
181+
Computed: true,
182+
},
183+
},
184+
BlockTypes: map[string]*configschema.NestedBlock{
185+
"config": {
186+
Block: *ret.Body,
187+
Nesting: configschema.NestingSingle,
188+
},
189+
},
190+
},
191+
}
174192
}
175193

176194
if decls, err := convert.FunctionDeclsFromProto(protoResp.Functions); err == nil {
@@ -357,7 +375,8 @@ func (p *GRPCProvider) ValidateListResourceConfig(r providers.ValidateListResour
357375
return resp
358376
}
359377

360-
mp, err := msgpack.Marshal(r.Config, listResourceSchema.Body.ImpliedType())
378+
configSchema := listResourceSchema.Body.BlockTypes["config"]
379+
mp, err := msgpack.Marshal(r.Config, configSchema.ImpliedType())
361380
if err != nil {
362381
resp.Diagnostics = resp.Diagnostics.Append(err)
363382
return resp
@@ -1274,7 +1293,8 @@ func (p *GRPCProvider) ListResource(r providers.ListResourceRequest) providers.L
12741293
return resp
12751294
}
12761295

1277-
mp, err := msgpack.Marshal(r.Config, listResourceSchema.Body.ImpliedType())
1296+
configSchema := listResourceSchema.Body.BlockTypes["config"]
1297+
mp, err := msgpack.Marshal(r.Config, configSchema.ImpliedType())
12781298
if err != nil {
12791299
resp.Diagnostics = resp.Diagnostics.Append(err)
12801300
return resp
@@ -1298,43 +1318,42 @@ func (p *GRPCProvider) ListResource(r providers.ListResourceRequest) providers.L
12981318
return resp
12991319
}
13001320

1301-
var results []providers.ListResourceEvent
1321+
resp.Result = cty.DynamicVal
1322+
results := make([]cty.Value, 0)
13021323
// Process the stream
13031324
for {
13041325
if int64(len(results)) >= r.Limit {
13051326
// If we have reached the limit, we stop receiving events
1306-
resp.Results = results
1307-
return resp
1327+
break
13081328
}
13091329

13101330
event, err := client.Recv()
13111331
if err == io.EOF {
13121332
// End of stream, we're done
1313-
resp.Results = results
1314-
return resp
1333+
break
13151334
}
13161335

13171336
if err != nil {
1318-
resp.Results = results
13191337
resp.Diagnostics = resp.Diagnostics.Append(err)
1320-
return resp
1338+
break
13211339
}
13221340

1323-
// Process the event
1324-
resourceEvent := providers.ListResourceEvent{
1325-
DisplayName: event.DisplayName,
1326-
Diagnostics: convert.ProtoToDiagnostics(event.Diagnostic),
1341+
obj := map[string]cty.Value{
1342+
"display_name": cty.StringVal(event.DisplayName),
1343+
"state": cty.NullVal(resourceSchema.Body.ImpliedType()),
1344+
"identity": cty.NullVal(resourceSchema.Identity.ImpliedType()),
13271345
}
1346+
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(event.Diagnostic))
13281347

13291348
// Handle identity data - it must be present
13301349
if event.Identity == nil || event.Identity.IdentityData == nil {
1331-
resourceEvent.Diagnostics = resourceEvent.Diagnostics.Append(fmt.Errorf("missing identity data in ListResource event for %s", r.TypeName))
1350+
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("missing identity data in ListResource event for %s", r.TypeName))
13321351
} else {
13331352
identityVal, err := decodeDynamicValue(event.Identity.IdentityData, resourceSchema.Identity.ImpliedType())
13341353
if err != nil {
1335-
resourceEvent.Diagnostics = resourceEvent.Diagnostics.Append(err)
1354+
resp.Diagnostics = resp.Diagnostics.Append(err)
13361355
} else {
1337-
resourceEvent.Identity = identityVal
1356+
obj["identity"] = identityVal
13381357
}
13391358
}
13401359

@@ -1343,14 +1362,26 @@ func (p *GRPCProvider) ListResource(r providers.ListResourceRequest) providers.L
13431362
// Use the ResourceTypes schema for the resource object
13441363
resourceObj, err := decodeDynamicValue(event.ResourceObject, resourceSchema.Body.ImpliedType())
13451364
if err != nil {
1346-
resourceEvent.Diagnostics = resourceEvent.Diagnostics.Append(err)
1365+
resp.Diagnostics = resp.Diagnostics.Append(err)
13471366
} else {
1348-
resourceEvent.ResourceObject = resourceObj
1367+
obj["state"] = resourceObj
13491368
}
13501369
}
13511370

1352-
results = append(results, resourceEvent)
1371+
if resp.Diagnostics.HasErrors() {
1372+
break
1373+
}
1374+
1375+
results = append(results, cty.ObjectVal(obj))
1376+
13531377
}
1378+
1379+
// The provider result of a list resource is always a list, but
1380+
// we will wrap that list in an object with a single attribute "data",
1381+
// so that we can differentiate between a list resource instance (list.aws_instance.test[index])
1382+
// and the elements of the result of a list resource instance (list.aws_instance.test.data[index])
1383+
resp.Result = cty.ObjectVal(map[string]cty.Value{"data": cty.TupleVal(results)})
1384+
return resp
13541385
}
13551386

13561387
func (p *GRPCProvider) ValidateStateStoreConfig(r providers.ValidateStateStoreConfigRequest) providers.ValidateStateStoreConfigResponse {

0 commit comments

Comments
 (0)