Skip to content

Commit 64fd972

Browse files
committed
Started adding ValidateListResourceConfig
1 parent 98bfa36 commit 64fd972

20 files changed

+635
-10
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ toolchain go1.23.7
66

77
require (
88
github.com/google/go-cmp v0.7.0
9-
github.com/hashicorp/terraform-plugin-go v0.28.0
9+
github.com/hashicorp/terraform-plugin-go v0.28.1-0.20250604150620-c6d59498a4e1
1010
github.com/hashicorp/terraform-plugin-log v0.9.0
11-
google.golang.org/grpc v1.72.1
11+
google.golang.org/grpc v1.72.2
1212
)
1313

1414
require (

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0U
2121
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
2222
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
2323
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
24-
github.com/hashicorp/terraform-plugin-go v0.28.0 h1:zJmu2UDwhVN0J+J20RE5huiF3XXlTYVIleaevHZgKPA=
25-
github.com/hashicorp/terraform-plugin-go v0.28.0/go.mod h1:FDa2Bb3uumkTGSkTFpWSOwWJDwA7bf3vdP3ltLDTH6o=
24+
github.com/hashicorp/terraform-plugin-go v0.28.1-0.20250604150620-c6d59498a4e1 h1:TbYGMFWFJDeGgLf5r/vZ8U7sV/f8dg3PR/tDDV4lw/I=
25+
github.com/hashicorp/terraform-plugin-go v0.28.1-0.20250604150620-c6d59498a4e1/go.mod h1:2t+AK4nKnRw39xPaCHHEMz79e8BIZoEWsSsVUkC8jVU=
2626
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
2727
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
2828
github.com/hashicorp/terraform-registry-address v0.2.5 h1:2GTftHqmUhVOeuu9CW3kwDkRe4pcBDq0uuK5VJngU1M=
@@ -80,8 +80,8 @@ golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
8080
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
8181
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
8282
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
83-
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
84-
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
83+
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
84+
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
8585
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
8686
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
8787
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

internal/tf5testserver/tf5testserver.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ type TestServer struct {
6464
ValidateDataSourceConfigCalled map[string]bool
6565

6666
ValidateResourceTypeConfigCalled map[string]bool
67+
68+
ValidateListResourceConfigCalled map[string]bool
6769
}
6870

6971
func (s *TestServer) ProviderServer() tfprotov5.ProviderServer {
@@ -269,6 +271,15 @@ func (s *TestServer) ValidateResourceTypeConfig(_ context.Context, req *tfprotov
269271
return nil, nil
270272
}
271273

274+
func (s *TestServer) ValidateListResourceConfig(_ context.Context, req *tfprotov5.ValidateListResourceConfigRequest) (*tfprotov5.ValidateListResourceConfigResponse, error) {
275+
if s.ValidateListResourceConfigCalled == nil {
276+
s.ValidateListResourceConfigCalled = make(map[string]bool)
277+
}
278+
279+
s.ValidateListResourceConfigCalled[req.TypeName] = true
280+
return nil, nil
281+
}
282+
272283
func (s *TestServer) PrepareProviderConfig(_ context.Context, req *tfprotov5.PrepareProviderConfigRequest) (*tfprotov5.PrepareProviderConfigResponse, error) {
273284
s.PrepareProviderConfigCalled = true
274285
return s.PrepareProviderConfigResponse, nil

internal/tf6testserver/tf6testserver.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ type TestServer struct {
6464
ValidateProviderConfigResponse *tfprotov6.ValidateProviderConfigResponse
6565

6666
ValidateResourceConfigCalled map[string]bool
67+
68+
ValidateListResourceConfigCalled map[string]bool
6769
}
6870

6971
func (s *TestServer) ProviderServer() tfprotov6.ProviderServer {
@@ -273,3 +275,12 @@ func (s *TestServer) ValidateProviderConfig(_ context.Context, req *tfprotov6.Va
273275
s.ValidateProviderConfigCalled = true
274276
return s.ValidateProviderConfigResponse, nil
275277
}
278+
279+
func (s *TestServer) ValidateListResourceConfig(_ context.Context, req *tfprotov6.ValidateListResourceConfigRequest) (*tfprotov6.ValidateListResourceConfigResponse, error) {
280+
if s.ValidateListResourceConfigCalled == nil {
281+
s.ValidateListResourceConfigCalled = make(map[string]bool)
282+
}
283+
284+
s.ValidateListResourceConfigCalled[req.TypeName] = true
285+
return nil, nil
286+
}

internal/tfprotov5tov6/tfprotov5tov6.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ func GetMetadataResponse(in *tfprotov5.GetMetadataResponse) *tfprotov6.GetMetada
298298
DataSources: make([]tfprotov6.DataSourceMetadata, 0, len(in.DataSources)),
299299
Diagnostics: Diagnostics(in.Diagnostics),
300300
EphemeralResources: make([]tfprotov6.EphemeralResourceMetadata, 0, len(in.Resources)),
301+
ListResources: make([]tfprotov6.ListResourceMetadata, 0, len(in.Resources)),
301302
Functions: make([]tfprotov6.FunctionMetadata, 0, len(in.Functions)),
302303
Resources: make([]tfprotov6.ResourceMetadata, 0, len(in.Resources)),
303304
ServerCapabilities: ServerCapabilities(in.ServerCapabilities),
@@ -311,6 +312,10 @@ func GetMetadataResponse(in *tfprotov5.GetMetadataResponse) *tfprotov6.GetMetada
311312
resp.EphemeralResources = append(resp.EphemeralResources, EphemeralResourceMetadata(ephemeralResource))
312313
}
313314

315+
for _, listResource := range in.ListResources {
316+
resp.ListResources = append(resp.ListResources, ListResourceMetadata(listResource))
317+
}
318+
314319
for _, function := range in.Functions {
315320
resp.Functions = append(resp.Functions, FunctionMetadata(function))
316321
}
@@ -347,6 +352,12 @@ func GetProviderSchemaResponse(in *tfprotov5.GetProviderSchemaResponse) *tfproto
347352
ephemeralResourceSchemas[k] = Schema(v)
348353
}
349354

355+
listResourceSchemas := make(map[string]*tfprotov6.Schema, len(in.ListResourceSchemas))
356+
357+
for k, v := range in.ListResourceSchemas {
358+
listResourceSchemas[k] = Schema(v)
359+
}
360+
350361
functions := make(map[string]*tfprotov6.Function, len(in.Functions))
351362

352363
for name, function := range in.Functions {
@@ -363,6 +374,7 @@ func GetProviderSchemaResponse(in *tfprotov5.GetProviderSchemaResponse) *tfproto
363374
DataSourceSchemas: dataSourceSchemas,
364375
Diagnostics: Diagnostics(in.Diagnostics),
365376
EphemeralResourceSchemas: ephemeralResourceSchemas,
377+
ListResourceSchemas: listResourceSchemas,
366378
Functions: functions,
367379
Provider: Schema(in.Provider),
368380
ProviderMeta: Schema(in.ProviderMeta),
@@ -977,3 +989,24 @@ func ValidateResourceConfigResponse(in *tfprotov5.ValidateResourceTypeConfigResp
977989
Diagnostics: Diagnostics(in.Diagnostics),
978990
}
979991
}
992+
993+
func ValidateListResourceConfigRequest(in *tfprotov5.ValidateListResourceConfigRequest) *tfprotov6.ValidateListResourceConfigRequest {
994+
if in == nil {
995+
return nil
996+
}
997+
998+
return &tfprotov6.ValidateListResourceConfigRequest{
999+
Config: DynamicValue(in.Config),
1000+
TypeName: in.TypeName,
1001+
}
1002+
}
1003+
1004+
func ValidateListResourceConfigResponse(in *tfprotov5.ValidateListResourceConfigResponse) *tfprotov6.ValidateListResourceConfigResponse {
1005+
if in == nil {
1006+
return nil
1007+
}
1008+
1009+
return &tfprotov6.ValidateListResourceConfigResponse{
1010+
Diagnostics: Diagnostics(in.Diagnostics),
1011+
}
1012+
}

internal/tfprotov6tov5/tfprotov6tov5.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ func GetMetadataResponse(in *tfprotov6.GetMetadataResponse) *tfprotov5.GetMetada
303303
DataSources: make([]tfprotov5.DataSourceMetadata, 0, len(in.DataSources)),
304304
Diagnostics: Diagnostics(in.Diagnostics),
305305
EphemeralResources: make([]tfprotov5.EphemeralResourceMetadata, 0, len(in.Resources)),
306+
ListResources: make([]tfprotov5.ListResourceMetadata, 0, len(in.Resources)),
306307
Functions: make([]tfprotov5.FunctionMetadata, 0, len(in.Functions)),
307308
Resources: make([]tfprotov5.ResourceMetadata, 0, len(in.Resources)),
308309
ServerCapabilities: ServerCapabilities(in.ServerCapabilities),
@@ -316,6 +317,10 @@ func GetMetadataResponse(in *tfprotov6.GetMetadataResponse) *tfprotov5.GetMetada
316317
resp.EphemeralResources = append(resp.EphemeralResources, EphemeralResourceMetadata(ephemeralResource))
317318
}
318319

320+
for _, listResource := range in.ListResources {
321+
resp.ListResources = append(resp.ListResources, ListResourceMetadata(listResource))
322+
}
323+
319324
for _, function := range in.Functions {
320325
resp.Functions = append(resp.Functions, FunctionMetadata(function))
321326
}
@@ -364,6 +369,18 @@ func GetProviderSchemaResponse(in *tfprotov6.GetProviderSchemaResponse) (*tfprot
364369
ephemeralResourceSchemas[k] = v5Schema
365370
}
366371

372+
listResourceSchemas := make(map[string]*tfprotov5.Schema, len(in.ListResourceSchemas))
373+
374+
for k, v := range in.ListResourceSchemas {
375+
v5Schema, err := Schema(v)
376+
377+
if err != nil {
378+
return nil, fmt.Errorf("unable to convert list resource %q schema: %w", k, err)
379+
}
380+
381+
listResourceSchemas[k] = v5Schema
382+
}
383+
367384
functions := make(map[string]*tfprotov5.Function, len(in.Functions))
368385

369386
for name, function := range in.Functions {
@@ -398,6 +415,7 @@ func GetProviderSchemaResponse(in *tfprotov6.GetProviderSchemaResponse) (*tfprot
398415
DataSourceSchemas: dataSourceSchemas,
399416
Diagnostics: Diagnostics(in.Diagnostics),
400417
EphemeralResourceSchemas: ephemeralResourceSchemas,
418+
ListResourceSchemas: listResourceSchemas,
401419
Functions: functions,
402420
Provider: provider,
403421
ProviderMeta: providerMeta,
@@ -1038,3 +1056,24 @@ func ValidateResourceTypeConfigResponse(in *tfprotov6.ValidateResourceConfigResp
10381056
Diagnostics: Diagnostics(in.Diagnostics),
10391057
}
10401058
}
1059+
1060+
func ValidateListResourceConfigRequest(in *tfprotov6.ValidateListResourceConfigRequest) *tfprotov5.ValidateListResourceConfigRequest {
1061+
if in == nil {
1062+
return nil
1063+
}
1064+
1065+
return &tfprotov5.ValidateListResourceConfigRequest{
1066+
Config: DynamicValue(in.Config),
1067+
TypeName: in.TypeName,
1068+
}
1069+
}
1070+
1071+
func ValidateListResourceConfigResponse(in *tfprotov6.ValidateListResourceConfigResponse) *tfprotov5.ValidateListResourceConfigResponse {
1072+
if in == nil {
1073+
return nil
1074+
}
1075+
1076+
return &tfprotov5.ValidateListResourceConfigResponse{
1077+
Diagnostics: Diagnostics(in.Diagnostics),
1078+
}
1079+
}

tf5muxserver/diagnostics.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,27 @@ func ephemeralResourceMissingError(typeName string) *tfprotov5.Diagnostic {
4747
}
4848
}
4949

50+
func listResourceDuplicateError(typeName string) *tfprotov5.Diagnostic {
51+
return &tfprotov5.Diagnostic{
52+
Severity: tfprotov5.DiagnosticSeverityError,
53+
Summary: "Invalid Provider Server Combination",
54+
Detail: "The combined provider has multiple implementations of the same list resource type across underlying providers. " +
55+
"List resource types must be implemented by only one underlying provider. " +
56+
"This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" +
57+
"Duplicate list resource type: " + typeName,
58+
}
59+
}
60+
61+
func listResourceMissingError(typeName string) *tfprotov5.Diagnostic {
62+
return &tfprotov5.Diagnostic{
63+
Severity: tfprotov5.DiagnosticSeverityError,
64+
Summary: "List Resource Not Implemented",
65+
Detail: "The combined provider does not implement the requested list resource type. " +
66+
"This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" +
67+
"Missing list resource type: " + typeName,
68+
}
69+
}
70+
5071
func diagnosticsHasError(diagnostics []*tfprotov5.Diagnostic) bool {
5172
for _, diagnostic := range diagnostics {
5273
if diagnostic == nil {

tf5muxserver/mux_server.go

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ type muxServer struct {
2626
// Routing for ephemeral resource types
2727
ephemeralResources map[string]tfprotov5.ProviderServer
2828

29+
// Routing for list resource types
30+
listResources map[string]tfprotov5.ProviderServer
31+
2932
// Routing for functions
3033
functions map[string]tfprotov5.ProviderServer
3134

@@ -129,6 +132,41 @@ func (s *muxServer) getEphemeralResourceServer(ctx context.Context, typeName str
129132
return server, s.serverDiscoveryDiagnostics, nil
130133
}
131134

135+
func (s *muxServer) getListResourceServer(ctx context.Context, typeName string) (tfprotov5.ProviderServer, []*tfprotov5.Diagnostic, error) {
136+
s.serverDiscoveryMutex.RLock()
137+
server, ok := s.listResources[typeName]
138+
discoveryComplete := s.serverDiscoveryComplete
139+
s.serverDiscoveryMutex.RUnlock()
140+
141+
if discoveryComplete {
142+
if ok {
143+
return server, s.serverDiscoveryDiagnostics, nil
144+
}
145+
146+
return nil, []*tfprotov5.Diagnostic{
147+
listResourceMissingError(typeName),
148+
}, nil
149+
}
150+
151+
err := s.serverDiscovery(ctx)
152+
153+
if err != nil || diagnosticsHasError(s.serverDiscoveryDiagnostics) {
154+
return nil, s.serverDiscoveryDiagnostics, err
155+
}
156+
157+
s.serverDiscoveryMutex.RLock()
158+
server, ok = s.listResources[typeName]
159+
s.serverDiscoveryMutex.RUnlock()
160+
161+
if !ok {
162+
return nil, []*tfprotov5.Diagnostic{
163+
listResourceMissingError(typeName),
164+
}, nil
165+
}
166+
167+
return server, s.serverDiscoveryDiagnostics, nil
168+
}
169+
132170
func (s *muxServer) getFunctionServer(ctx context.Context, name string) (tfprotov5.ProviderServer, []*tfprotov5.Diagnostic, error) {
133171
s.serverDiscoveryMutex.RLock()
134172
server, ok := s.functions[name]
@@ -202,7 +240,8 @@ func (s *muxServer) getResourceServer(ctx context.Context, typeName string) (tfp
202240
// serverDiscovery will populate the mux server "routing" for functions and
203241
// resource types by calling all underlying server GetMetadata RPC and falling
204242
// back to GetProviderSchema RPC. It is intended to only be called through
205-
// getDataSourceServer, getEphemeralResourceServer, getFunctionServer, and getResourceServer.
243+
// getDataSourceServer, getEphemeralResourceServer, getListResourceServer,
244+
// getFunctionServer, and getResourceServer.
206245
//
207246
// The error return represents gRPC errors, which except for the GetMetadata
208247
// call returning the gRPC unimplemented error, is always returned.
@@ -250,6 +289,16 @@ func (s *muxServer) serverDiscovery(ctx context.Context) error {
250289
s.ephemeralResources[serverEphemeralResource.TypeName] = server
251290
}
252291

292+
for _, serverListResource := range metadataResp.ListResources {
293+
if _, ok := s.listResources[serverListResource.TypeName]; ok {
294+
s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, listResourceDuplicateError(serverListResource.TypeName))
295+
296+
continue
297+
}
298+
299+
s.listResources[serverListResource.TypeName] = server
300+
}
301+
253302
for _, serverFunction := range metadataResp.Functions {
254303
if _, ok := s.functions[serverFunction.Name]; ok {
255304
s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, functionDuplicateError(serverFunction.Name))
@@ -312,6 +361,16 @@ func (s *muxServer) serverDiscovery(ctx context.Context) error {
312361
s.ephemeralResources[typeName] = server
313362
}
314363

364+
for typeName := range providerSchemaResp.ListResourceSchemas {
365+
if _, ok := s.listResources[typeName]; ok {
366+
s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, listResourceDuplicateError(typeName))
367+
368+
continue
369+
}
370+
371+
s.listResources[typeName] = server
372+
}
373+
315374
for name := range providerSchemaResp.Functions {
316375
if _, ok := s.functions[name]; ok {
317376
s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, functionDuplicateError(name))
@@ -349,11 +408,13 @@ func (s *muxServer) serverDiscovery(ctx context.Context) error {
349408
// - Only one provider implements each data source
350409
// - Only one provider implements each function
351410
// - Only one provider implements each ephemeral resource
411+
// - Only one provider implements each list resource
352412
// - Only one provider implements each resource identity
353413
func NewMuxServer(_ context.Context, servers ...func() tfprotov5.ProviderServer) (*muxServer, error) {
354414
result := muxServer{
355415
dataSources: make(map[string]tfprotov5.ProviderServer),
356416
ephemeralResources: make(map[string]tfprotov5.ProviderServer),
417+
listResources: make(map[string]tfprotov5.ProviderServer),
357418
functions: make(map[string]tfprotov5.ProviderServer),
358419
resources: make(map[string]tfprotov5.ProviderServer),
359420
resourceCapabilities: make(map[string]*tfprotov5.ServerCapabilities),

0 commit comments

Comments
 (0)