diff --git a/go.mod b/go.mod index 1c04b16..d0c1e2f 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ toolchain go1.24.4 require ( github.com/google/go-cmp v0.7.0 - github.com/hashicorp/terraform-plugin-go v0.29.0 + github.com/hashicorp/terraform-plugin-go v0.29.1-0.20251112131031-c841d34ce2f2 github.com/hashicorp/terraform-plugin-log v0.9.0 - google.golang.org/grpc v1.75.1 + google.golang.org/grpc v1.76.0 ) require ( @@ -29,6 +29,6 @@ require ( golang.org/x/net v0.43.0 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect - google.golang.org/protobuf v1.36.9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect + google.golang.org/protobuf v1.36.10 // indirect ) diff --git a/go.sum b/go.sum index 2a51332..b91bce4 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshf github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU= -github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM= +github.com/hashicorp/terraform-plugin-go v0.29.1-0.20251112131031-c841d34ce2f2 h1:GxLILx5hl084NZFVBc6rdzKrJ5DL8MYNaQapmXFcOHY= +github.com/hashicorp/terraform-plugin-go v0.29.1-0.20251112131031-c841d34ce2f2/go.mod h1:KHRnT9vExG+r1fLxwOzOP6C6YXiaKHtsCIrky7teDYc= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= @@ -86,12 +86,12 @@ golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= -google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tf5muxserver/diagnostics.go b/tf5muxserver/diagnostics.go index 406120f..8cd952f 100644 --- a/tf5muxserver/diagnostics.go +++ b/tf5muxserver/diagnostics.go @@ -68,6 +68,16 @@ func ephemeralResourceMissingError(typeName string) *tfprotov5.Diagnostic { } } +func generateResourceConfigMissingError(typeName string) *tfprotov5.Diagnostic { + return &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Generate Resource Config Not Implemented", + Detail: "The combined provider does not implement the requested generate resource config for the resource type. " + + "This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" + + "Missing generate resource config for resource type: " + typeName, + } +} + func listResourceDuplicateError(typeName string) *tfprotov5.Diagnostic { return &tfprotov5.Diagnostic{ Severity: tfprotov5.DiagnosticSeverityError, diff --git a/tf5muxserver/mux_server.go b/tf5muxserver/mux_server.go index 0122000..6bdfe51 100644 --- a/tf5muxserver/mux_server.go +++ b/tf5muxserver/mux_server.go @@ -7,10 +7,10 @@ import ( "context" "sync" - "github.com/hashicorp/terraform-plugin-go/tfprotov5" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" "github.com/hashicorp/terraform-plugin-mux/internal/logging" ) @@ -65,6 +65,10 @@ func (s *muxServer) ProviderServer() tfprotov5.ProviderServer { return s } +func (s *muxServer) ProviderServers() []tfprotov5.ProviderServer { + return s.servers +} + func (s *muxServer) getActionServer(ctx context.Context, actionType string) (tfprotov5.ProviderServer, []*tfprotov5.Diagnostic, error) { s.serverDiscoveryMutex.RLock() server, ok := s.actions[actionType] @@ -170,6 +174,41 @@ func (s *muxServer) getEphemeralResourceServer(ctx context.Context, typeName str return server, s.serverDiscoveryDiagnostics, nil } +func (s *muxServer) getGenerateResourceConfigServer(ctx context.Context, typeName string) (tfprotov5.ProviderServer, []*tfprotov5.Diagnostic, error) { + s.serverDiscoveryMutex.RLock() + server, ok := s.resources[typeName] + discoveryComplete := s.serverDiscoveryComplete + s.serverDiscoveryMutex.RUnlock() + + if discoveryComplete { + if ok { + return server, s.serverDiscoveryDiagnostics, nil + } + + return nil, []*tfprotov5.Diagnostic{ + generateResourceConfigMissingError(typeName), + }, nil + } + + err := s.serverDiscovery(ctx) + + if err != nil || diagnosticsHasError(s.serverDiscoveryDiagnostics) { + return nil, s.serverDiscoveryDiagnostics, err + } + + s.serverDiscoveryMutex.RLock() + server, ok = s.resources[typeName] + s.serverDiscoveryMutex.RUnlock() + + if !ok { + return nil, []*tfprotov5.Diagnostic{ + generateResourceConfigMissingError(typeName), + }, nil + } + + return server, s.serverDiscoveryDiagnostics, nil +} + func (s *muxServer) getListResourceServer(ctx context.Context, typeName string) (tfprotov5.ProviderServer, []*tfprotov5.Diagnostic, error) { s.serverDiscoveryMutex.RLock() server, ok := s.listResources[typeName] diff --git a/tf5muxserver/mux_server_GenerateResourceConfig.go b/tf5muxserver/mux_server_GenerateResourceConfig.go new file mode 100644 index 0000000..2485157 --- /dev/null +++ b/tf5muxserver/mux_server_GenerateResourceConfig.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf5muxserver + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-mux/internal/logging" +) + +func (s *muxServer) GenerateResourceConfig(ctx context.Context, req *tfprotov5.GenerateResourceConfigRequest) (*tfprotov5.GenerateResourceConfigResponse, error) { + rpc := "GenerateResourceConfig" + ctx = logging.InitContext(ctx) + ctx = logging.RpcContext(ctx, rpc) + + server, diags, err := s.getGenerateResourceConfigServer(ctx, req.TypeName) + + if err != nil { + return nil, err + } + + // If there is an error diagnostic, return it directly + if diagnosticsHasError(diags) { + return &tfprotov5.GenerateResourceConfigResponse{ + Diagnostics: diags, + }, nil + } + + // TODO: Remove and call server.GenerateResourceConfig below directly once interface becomes required. + generateResourceConfigServer, ok := server.(tfprotov5.GenerateResourceConfigServer) + if !ok { + resp := &tfprotov5.GenerateResourceConfigResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "GenerateResourceConfig Not Implemented", + Detail: "A GenerateResourceConfig call was received by the provider, however the provider does not implement GenerateResourceConfig. " + + "Either upgrade the provider to a version that implements GenerateResourceConfig or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return resp, nil + } + + ctx = logging.Tfprotov5ProviderServerContext(ctx, server) + logging.MuxTrace(ctx, "calling downstream server") + + return generateResourceConfigServer.GenerateResourceConfig(ctx, req) +} diff --git a/tf5muxserver/mux_server_GetMetadata_test.go b/tf5muxserver/mux_server_GetMetadata_test.go index 1463d57..96aceb6 100644 --- a/tf5muxserver/mux_server_GetMetadata_test.go +++ b/tf5muxserver/mux_server_GetMetadata_test.go @@ -187,6 +187,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-action": { @@ -234,6 +235,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-data-source-type": { @@ -281,6 +283,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-ephemeral-resource-type": { @@ -328,6 +331,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-list-resource-type": { @@ -375,6 +379,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-function": { @@ -422,6 +427,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-resource-type": { @@ -469,6 +475,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "server-capabilities": { @@ -514,6 +521,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "error-once": { @@ -549,6 +557,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "error-multiple": { @@ -599,6 +608,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "warning-once": { @@ -634,6 +644,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "warning-multiple": { @@ -684,6 +695,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "warning-then-error": { @@ -734,6 +746,7 @@ func TestMuxServerGetMetadata(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, } diff --git a/tf5muxserver/mux_server_GetProviderSchema_test.go b/tf5muxserver/mux_server_GetProviderSchema_test.go index f675463..0654b21 100644 --- a/tf5muxserver/mux_server_GetProviderSchema_test.go +++ b/tf5muxserver/mux_server_GetProviderSchema_test.go @@ -797,6 +797,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-action": { @@ -838,6 +839,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-data-source-type": { @@ -879,6 +881,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-ephemeral-resource-type": { @@ -920,6 +923,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-list-resource-type": { @@ -961,6 +965,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-function": { @@ -1002,6 +1007,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "duplicate-resource-type": { @@ -1043,6 +1049,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "provider-mismatch": { @@ -1132,6 +1139,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "provider-meta-mismatch": { @@ -1221,6 +1229,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "server-capabilities": { @@ -1258,6 +1267,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "error-once": { @@ -1293,6 +1303,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "error-multiple": { @@ -1343,6 +1354,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "warning-once": { @@ -1378,6 +1390,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "warning-multiple": { @@ -1428,6 +1441,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, "warning-then-error": { @@ -1478,6 +1492,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, }, }, } diff --git a/tf5muxserver/server_capabilities.go b/tf5muxserver/server_capabilities.go index 390bf42..dd47c3d 100644 --- a/tf5muxserver/server_capabilities.go +++ b/tf5muxserver/server_capabilities.go @@ -12,6 +12,7 @@ var serverCapabilities = &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, PlanDestroy: true, + GenerateResourceConfig: true, } // serverSupportsPlanDestroy returns true if the given ServerCapabilities is not