Skip to content

Commit 677a273

Browse files
authored
Merge pull request #44129 from hashicorp/f-list_sdkv2
List Resource initial SDKv2 implementation
2 parents e338dda + 978fd90 commit 677a273

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+3081
-840
lines changed

.changelog/44129.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
```release-note:enhancement
2+
resource/aws_instance: Adds List support
3+
```
4+
5+
```release-note:enhancement
6+
resource/aws_iam_role: Adds List support
7+
```
8+
9+
```release-note:enhancement
10+
resource/aws_cloudwatch_log_group: Adds List support
11+
```

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ require (
299299
github.com/hashicorp/terraform-plugin-go v0.29.0
300300
github.com/hashicorp/terraform-plugin-log v0.9.0
301301
github.com/hashicorp/terraform-plugin-mux v0.21.0
302-
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0
303-
github.com/hashicorp/terraform-plugin-testing v1.13.3-0.20250917121907-cca15217581a
302+
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.0
303+
github.com/hashicorp/terraform-plugin-testing v1.14.0-beta.1
304304
github.com/jaswdr/faker/v2 v2.8.0
305305
github.com/jmespath/go-jmespath v0.4.0
306306
github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -683,10 +683,10 @@ github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+
683683
github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM=
684684
github.com/hashicorp/terraform-plugin-mux v0.21.0 h1:QsEYnzSD2c3zT8zUrUGqaFGhV/Z8zRUlU7FY3ZPJFfw=
685685
github.com/hashicorp/terraform-plugin-mux v0.21.0/go.mod h1:Qpt8+6AD7NmL0DS7ASkN0EXpDQ2J/FnnIgeUr1tzr5A=
686-
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0 h1:NFPMacTrY/IdcIcnUB+7hsore1ZaRWU9cnB6jFoBnIM=
687-
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0/go.mod h1:QYmYnLfsosrxjCnGY1p9c7Zj6n9thnEE+7RObeYs3fA=
688-
github.com/hashicorp/terraform-plugin-testing v1.13.3-0.20250917121907-cca15217581a h1:ZR7fP9+RhyyPG3Pt0cyJLGv+RhwgM2Djw6bpR6lZTSY=
689-
github.com/hashicorp/terraform-plugin-testing v1.13.3-0.20250917121907-cca15217581a/go.mod h1:EutH7GqQVuQ93JFgTfWFiytgzpGzsqx3aUMcssK5V8s=
686+
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.0 h1:PQP7Crrc7t/ozj+P9x0/lsTzGNy3lVppH8zAJylofaE=
687+
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.0/go.mod h1:GQhpKVvvuwzD79e8/NZ+xzj+ZpWovdPAe8nfV/skwNU=
688+
github.com/hashicorp/terraform-plugin-testing v1.14.0-beta.1 h1:caWmY2Fv/KgDAXU7IVjcBDfIdmr/n6VRYhCLxNmlaXs=
689+
github.com/hashicorp/terraform-plugin-testing v1.14.0-beta.1/go.mod h1:jVm3pD9uQAT0X2RSEdcqjju2bCGv5f73DGZFU4v7EAU=
690690
github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk=
691691
github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE=
692692
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
@@ -755,8 +755,8 @@ github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXq
755755
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
756756
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
757757
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
758-
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
759-
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
758+
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
759+
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
760760
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
761761
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
762762
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package statecheck
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
"github.com/aws/aws-sdk-go-v2/aws/arn"
11+
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
12+
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
13+
)
14+
15+
var _ knownvalue.Check = globalARNExact{}
16+
17+
type globalARNExact struct {
18+
service string
19+
resource string
20+
}
21+
22+
// CheckValue determines whether the passed value is of type string, and
23+
// contains a matching sequence of bytes.
24+
func (v globalARNExact) CheckValue(other any) error {
25+
otherVal, ok := other.(string)
26+
27+
if !ok {
28+
return fmt.Errorf("expected string value for GlobalARNExact check, got: %T", other)
29+
}
30+
31+
if otherVal != v.buildARNString() {
32+
return fmt.Errorf("expected value %s for GlobalARNExact check, got: %s", v.buildARNString(), otherVal)
33+
}
34+
35+
return nil
36+
}
37+
38+
// String returns the string representation of the value.
39+
func (v globalARNExact) String() string {
40+
return v.buildARNString()
41+
}
42+
43+
func (v globalARNExact) buildARNString() string {
44+
return arn.ARN{
45+
AccountID: acctest.AccountID(context.Background()),
46+
Partition: acctest.Partition(),
47+
Region: "",
48+
Service: v.service,
49+
Resource: v.resource,
50+
}.String()
51+
}
52+
53+
func GlobalARNExact(service, resource string) knownvalue.Check {
54+
return globalARNExact{
55+
service: service,
56+
resource: resource,
57+
}
58+
}

internal/conns/conns.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ type ServicePackageWithFrameworkListResources interface {
4040
FrameworkListResources(context.Context) iter.Seq[*types.ServicePackageFrameworkListResource]
4141
}
4242

43+
type ServicePackageWithSDKListResources interface {
44+
ServicePackage
45+
SDKListResources(ctx context.Context) iter.Seq[*types.ServicePackageSDKListResource]
46+
}
47+
4348
type (
4449
contextKeyType int
4550
)

internal/errs/fwdiag/diags.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/hashicorp/terraform-plugin-framework/diag"
12+
"github.com/hashicorp/terraform-plugin-framework/list"
1213
)
1314

1415
// DiagnosticsError returns an error containing all Diagnostic with SeverityError
@@ -59,6 +60,17 @@ func NewResourceNotFoundWarningDiagnostic(err error) diag.Diagnostic {
5960
)
6061
}
6162

63+
func NewListResultErrorDiagnostic(err error) list.ListResult {
64+
return list.ListResult{
65+
Diagnostics: diag.Diagnostics{
66+
diag.NewErrorDiagnostic(
67+
"Error Listing Remote Resources",
68+
err.Error(),
69+
),
70+
},
71+
}
72+
}
73+
6274
func AsError[T any](x T, diags diag.Diagnostics) (T, error) {
6375
return x, DiagnosticsError(diags)
6476
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package framework
5+
6+
import (
7+
"context"
8+
"unique"
9+
10+
"github.com/hashicorp/terraform-plugin-framework/diag"
11+
"github.com/hashicorp/terraform-plugin-framework/list"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
14+
"github.com/hashicorp/terraform-provider-aws/internal/conns"
15+
inttypes "github.com/hashicorp/terraform-provider-aws/internal/types"
16+
tfunique "github.com/hashicorp/terraform-provider-aws/internal/unique"
17+
"github.com/hashicorp/terraform-provider-aws/names"
18+
)
19+
20+
type WithRegionSpec interface {
21+
SetRegionSpec(regionSpec unique.Handle[inttypes.ServicePackageResourceRegion])
22+
}
23+
24+
type ListResourceWithSDKv2Resource struct {
25+
resourceSchema *schema.Resource
26+
identitySpec inttypes.Identity
27+
identitySchema *schema.ResourceIdentity
28+
regionSpec unique.Handle[inttypes.ServicePackageResourceRegion]
29+
}
30+
31+
func (l *ListResourceWithSDKv2Resource) SetRegionSpec(regionSpec unique.Handle[inttypes.ServicePackageResourceRegion]) {
32+
l.regionSpec = regionSpec
33+
34+
var isRegionOverrideEnabled bool
35+
if !tfunique.IsHandleNil(regionSpec) && regionSpec.Value().IsOverrideEnabled {
36+
isRegionOverrideEnabled = true
37+
}
38+
39+
if isRegionOverrideEnabled {
40+
if _, ok := l.resourceSchema.SchemaMap()[names.AttrRegion]; !ok {
41+
// TODO: Use standard shared `region` attribute
42+
l.resourceSchema.SchemaMap()[names.AttrRegion] = &schema.Schema{
43+
Type: schema.TypeString,
44+
Optional: true,
45+
Computed: true,
46+
}
47+
}
48+
}
49+
}
50+
51+
func (l *ListResourceWithSDKv2Resource) SetIdentitySpec(identitySpec inttypes.Identity) {
52+
out := make(map[string]*schema.Schema)
53+
for _, v := range identitySpec.Attributes {
54+
out[v.Name()] = &schema.Schema{
55+
Type: schema.TypeString,
56+
}
57+
if v.Required() {
58+
out[v.Name()].Required = true
59+
} else {
60+
out[v.Name()].Optional = true
61+
}
62+
}
63+
64+
identitySchema := schema.ResourceIdentity{
65+
SchemaFunc: func() map[string]*schema.Schema {
66+
return out
67+
},
68+
}
69+
70+
l.identitySchema = &identitySchema
71+
l.resourceSchema.Identity = &identitySchema
72+
l.identitySpec = identitySpec
73+
}
74+
75+
func (l *ListResourceWithSDKv2Resource) RawV5Schemas(ctx context.Context, _ list.RawV5SchemaRequest, response *list.RawV5SchemaResponse) {
76+
response.ProtoV5Schema = l.resourceSchema.ProtoSchema(ctx)()
77+
response.ProtoV5IdentitySchema = l.resourceSchema.ProtoIdentitySchema(ctx)()
78+
}
79+
80+
func (l *ListResourceWithSDKv2Resource) SetResourceSchema(resource *schema.Resource) {
81+
l.resourceSchema = resource
82+
}
83+
84+
func (l *ListResourceWithSDKv2Resource) ResourceData() *schema.ResourceData {
85+
return l.resourceSchema.Data(&terraform.InstanceState{})
86+
}
87+
88+
func (l *ListResourceWithSDKv2Resource) setResourceIdentity(ctx context.Context, client *conns.AWSClient, d *schema.ResourceData) error {
89+
identity, err := d.Identity()
90+
if err != nil {
91+
return err
92+
}
93+
94+
for _, attr := range l.identitySpec.Attributes {
95+
switch attr.Name() {
96+
case names.AttrAccountID:
97+
if err := identity.Set(attr.Name(), client.AccountID(ctx)); err != nil {
98+
return err
99+
}
100+
101+
case names.AttrRegion:
102+
if err := identity.Set(attr.Name(), client.Region(ctx)); err != nil {
103+
return err
104+
}
105+
106+
default:
107+
val, ok := getAttributeOk(d, attr.ResourceAttributeName())
108+
if !ok {
109+
continue
110+
}
111+
if err := identity.Set(attr.Name(), val); err != nil {
112+
return err
113+
}
114+
}
115+
}
116+
117+
return nil
118+
}
119+
120+
type resourceData interface {
121+
Id() string
122+
GetOk(string) (any, bool)
123+
}
124+
125+
func getAttributeOk(d resourceData, name string) (string, bool) {
126+
if name == "id" {
127+
return d.Id(), true
128+
}
129+
if v, ok := d.GetOk(name); !ok {
130+
return "", false
131+
} else {
132+
return v.(string), true
133+
}
134+
}
135+
136+
func (l *ListResourceWithSDKv2Resource) SetResult(ctx context.Context, awsClient *conns.AWSClient, includeResource bool, result *list.ListResult, rd *schema.ResourceData) {
137+
err := l.setResourceIdentity(ctx, awsClient, rd)
138+
if err != nil {
139+
result.Diagnostics.Append(diag.NewErrorDiagnostic(
140+
"Error Listing Remote Resources",
141+
"An unexpected error occurred setting resource identity. "+
142+
"This is always an error in the provider. "+
143+
"Please report the following to the provider developer:\n\n"+
144+
"Error: "+err.Error(),
145+
))
146+
return
147+
}
148+
149+
tfTypeIdentity, err := rd.TfTypeIdentityState()
150+
if err != nil {
151+
result.Diagnostics.Append(diag.NewErrorDiagnostic(
152+
"Error Listing Remote Resources",
153+
"An unexpected error occurred converting identity state. "+
154+
"This is always an error in the provider. "+
155+
"Please report the following to the provider developer:\n\n"+
156+
"Error: "+err.Error(),
157+
))
158+
return
159+
}
160+
161+
result.Diagnostics.Append(result.Identity.Set(ctx, *tfTypeIdentity)...)
162+
if result.Diagnostics.HasError() {
163+
return
164+
}
165+
166+
if includeResource {
167+
if !tfunique.IsHandleNil(l.regionSpec) && l.regionSpec.Value().IsOverrideEnabled {
168+
if err := rd.Set(names.AttrRegion, awsClient.Region(ctx)); err != nil {
169+
result.Diagnostics.Append(diag.NewErrorDiagnostic(
170+
"Error Listing Remote Resources",
171+
"An unexpected error occurred. "+
172+
"This is always an error in the provider. "+
173+
"Please report the following to the provider developer:\n\n"+
174+
"Error: "+err.Error(),
175+
))
176+
return
177+
}
178+
}
179+
180+
tfTypeResource, err := rd.TfTypeResourceState()
181+
if err != nil {
182+
result.Diagnostics.Append(diag.NewErrorDiagnostic(
183+
"Error Listing Remote Resources",
184+
"An unexpected error occurred converting resource state. "+
185+
"This is always an error in the provider. "+
186+
"Please report the following to the provider developer:\n\n"+
187+
"Error: "+err.Error(),
188+
))
189+
return
190+
}
191+
192+
result.Diagnostics.Append(result.Resource.Set(ctx, *tfTypeResource)...)
193+
if result.Diagnostics.HasError() {
194+
return
195+
}
196+
}
197+
}

0 commit comments

Comments
 (0)