From e508bffd75f04a707a97edf76d857314a62aa53a Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 19 Aug 2024 14:31:35 -0400 Subject: [PATCH 01/10] Update `terraform-plugin-go` dependency to `SBGoods/ephemeral-resources` branch --- go.mod | 20 ++++++++++---------- go.sum | 9 +++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 8654b84..2f466e7 100644 --- a/go.mod +++ b/go.mod @@ -1,21 +1,21 @@ module github.com/hashicorp/terraform-plugin-mux -go 1.21 +go 1.22 -toolchain go1.21.6 +toolchain go1.22.6 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-go v0.23.0 + github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240819181557-bbf46acb5208 github.com/hashicorp/terraform-plugin-log v0.9.0 - google.golang.org/grpc v1.63.2 + google.golang.org/grpc v1.65.0 ) require ( github.com/fatih/color v1.13.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect - github.com/hashicorp/go-plugin v1.6.0 // indirect + github.com/hashicorp/go-plugin v1.6.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect @@ -26,9 +26,9 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/protobuf v1.34.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/protobuf v1.34.2 // indirect ) diff --git a/go.sum b/go.sum index 352a8f8..c070bc8 100644 --- a/go.sum +++ b/go.sum @@ -13,10 +13,13 @@ github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+ github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= +github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= 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.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co= github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ= +github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240819181557-bbf46acb5208 h1:ky9zy4izz4XTn0uW3aSls6aiQdRYBCFoHPPsH8+fSFI= +github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240819181557-bbf46acb5208/go.mod h1:ko0HcPe7AkwMukddKa13Qq5zyvi8V9KYxBZmqU8a9cE= 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.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= @@ -48,6 +51,7 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -55,14 +59,19 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 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= From f65ff7bbfd9a1c526ef9dbd00f1e3d2d95a27065 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Wed, 21 Aug 2024 15:27:38 -0400 Subject: [PATCH 02/10] Implement ephemeral resources in `tf5muxserver`, `tf6muxserver`, `tf6to5server`, and `tf5to6server`. --- go.sum | 23 +- internal/tf5testserver/tf5testserver.go | 44 +++ internal/tf6testserver/tf6testserver.go | 44 +++ internal/tfprotov5tov6/tfprotov5tov6.go | 126 +++++- internal/tfprotov5tov6/tfprotov5tov6_test.go | 337 ++++++++++++++++ internal/tfprotov6tov5/tfprotov6tov5.go | 130 ++++++- internal/tfprotov6tov5/tfprotov6tov5_test.go | 363 ++++++++++++++++++ tf5muxserver/diagnostics.go | 21 + tf5muxserver/mux_server.go | 65 +++- .../mux_server_CloseEphemeralResource.go | 35 ++ .../mux_server_CloseEphemeralResource_test.go | 72 ++++ tf5muxserver/mux_server_GetMetadata.go | 26 +- tf5muxserver/mux_server_GetMetadata_test.go | 109 +++++- tf5muxserver/mux_server_GetProviderSchema.go | 24 +- .../mux_server_GetProviderSchema_test.go | 211 ++++++++-- .../mux_server_OpenEphemeralResource.go | 35 ++ .../mux_server_OpenEphemeralResource_test.go | 72 ++++ .../mux_server_RenewEphemeralResource.go | 35 ++ .../mux_server_RenewEphemeralResource_test.go | 72 ++++ ..._server_ValidateEphemeralResourceConfig.go | 35 ++ ...er_ValidateEphemeralResourceConfig_test.go | 72 ++++ tf5to6server/tf5to6server.go | 44 +++ tf5to6server/tf5to6server_test.go | 127 ++++++ tf6muxserver/diagnostics.go | 21 + tf6muxserver/mux_server.go | 65 +++- .../mux_server_CloseEphemeralResource.go | 35 ++ .../mux_server_CloseEphemeralResource_test.go | 72 ++++ tf6muxserver/mux_server_GetMetadata.go | 22 ++ tf6muxserver/mux_server_GetMetadata_test.go | 109 +++++- tf6muxserver/mux_server_GetProviderSchema.go | 20 +- .../mux_server_GetProviderSchema_test.go | 214 +++++++++-- .../mux_server_OpenEphemeralResource.go | 35 ++ .../mux_server_OpenEphemeralResource_test.go | 72 ++++ .../mux_server_RenewEphemeralResource.go | 35 ++ .../mux_server_RenewEphemeralResource_test.go | 72 ++++ ..._server_ValidateEphemeralResourceConfig.go | 35 ++ ...er_ValidateEphemeralResourceConfig_test.go | 72 ++++ tf6to5server/tf6to5server.go | 44 +++ tf6to5server/tf6to5server_test.go | 127 ++++++ 39 files changed, 3054 insertions(+), 118 deletions(-) create mode 100644 tf5muxserver/mux_server_CloseEphemeralResource.go create mode 100644 tf5muxserver/mux_server_CloseEphemeralResource_test.go create mode 100644 tf5muxserver/mux_server_OpenEphemeralResource.go create mode 100644 tf5muxserver/mux_server_OpenEphemeralResource_test.go create mode 100644 tf5muxserver/mux_server_RenewEphemeralResource.go create mode 100644 tf5muxserver/mux_server_RenewEphemeralResource_test.go create mode 100644 tf5muxserver/mux_server_ValidateEphemeralResourceConfig.go create mode 100644 tf5muxserver/mux_server_ValidateEphemeralResourceConfig_test.go create mode 100644 tf6muxserver/mux_server_CloseEphemeralResource.go create mode 100644 tf6muxserver/mux_server_CloseEphemeralResource_test.go create mode 100644 tf6muxserver/mux_server_OpenEphemeralResource.go create mode 100644 tf6muxserver/mux_server_OpenEphemeralResource_test.go create mode 100644 tf6muxserver/mux_server_RenewEphemeralResource.go create mode 100644 tf6muxserver/mux_server_RenewEphemeralResource_test.go create mode 100644 tf6muxserver/mux_server_ValidateEphemeralResourceConfig.go create mode 100644 tf6muxserver/mux_server_ValidateEphemeralResourceConfig_test.go diff --git a/go.sum b/go.sum index c070bc8..3c312b9 100644 --- a/go.sum +++ b/go.sum @@ -11,13 +11,10 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= -github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= +github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= 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.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co= -github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ= github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240819181557-bbf46acb5208 h1:ky9zy4izz4XTn0uW3aSls6aiQdRYBCFoHPPsH8+fSFI= github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240819181557-bbf46acb5208/go.mod h1:ko0HcPe7AkwMukddKa13Qq5zyvi8V9KYxBZmqU8a9cE= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= @@ -49,28 +46,22 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= -google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/tf5testserver/tf5testserver.go b/internal/tf5testserver/tf5testserver.go index 1f810fa..83b11de 100644 --- a/internal/tf5testserver/tf5testserver.go +++ b/internal/tf5testserver/tf5testserver.go @@ -18,6 +18,8 @@ type TestServer struct { CallFunctionCalled map[string]bool + CloseEphemeralResourceCalled map[string]bool + ConfigureProviderCalled bool ConfigureProviderResponse *tfprotov5.ConfigureProviderResponse @@ -34,6 +36,8 @@ type TestServer struct { MoveResourceStateCalled map[string]bool + OpenEphemeralResourceCalled map[string]bool + PlanResourceChangeCalled map[string]bool PrepareProviderConfigCalled bool @@ -43,11 +47,15 @@ type TestServer struct { ReadResourceCalled map[string]bool + RenewEphemeralResourceCalled map[string]bool + StopProviderCalled bool StopProviderResponse *tfprotov5.StopProviderResponse UpgradeResourceStateCalled map[string]bool + ValidateEphemeralResourceConfigCalled map[string]bool + ValidateDataSourceConfigCalled map[string]bool ValidateResourceTypeConfigCalled map[string]bool @@ -75,6 +83,15 @@ func (s *TestServer) CallFunction(_ context.Context, req *tfprotov5.CallFunction return nil, nil } +func (s *TestServer) CloseEphemeralResource(ctx context.Context, req *tfprotov5.CloseEphemeralResourceRequest) (*tfprotov5.CloseEphemeralResourceResponse, error) { + if s.CloseEphemeralResourceCalled == nil { + s.CloseEphemeralResourceCalled = make(map[string]bool) + } + + s.CloseEphemeralResourceCalled[req.TypeName] = true + return nil, nil +} + func (s *TestServer) ConfigureProvider(_ context.Context, _ *tfprotov5.ConfigureProviderRequest) (*tfprotov5.ConfigureProviderResponse, error) { s.ConfigureProviderCalled = true @@ -137,6 +154,15 @@ func (s *TestServer) MoveResourceState(_ context.Context, req *tfprotov5.MoveRes return nil, nil } +func (s *TestServer) OpenEphemeralResource(_ context.Context, req *tfprotov5.OpenEphemeralResourceRequest) (*tfprotov5.OpenEphemeralResourceResponse, error) { + if s.OpenEphemeralResourceCalled == nil { + s.OpenEphemeralResourceCalled = make(map[string]bool) + } + + s.OpenEphemeralResourceCalled[req.TypeName] = true + return nil, nil +} + func (s *TestServer) PlanResourceChange(_ context.Context, req *tfprotov5.PlanResourceChangeRequest) (*tfprotov5.PlanResourceChangeResponse, error) { if s.PlanResourceChangeCalled == nil { s.PlanResourceChangeCalled = make(map[string]bool) @@ -164,6 +190,15 @@ func (s *TestServer) ReadResource(_ context.Context, req *tfprotov5.ReadResource return nil, nil } +func (s *TestServer) RenewEphemeralResource(_ context.Context, req *tfprotov5.RenewEphemeralResourceRequest) (*tfprotov5.RenewEphemeralResourceResponse, error) { + if s.RenewEphemeralResourceCalled == nil { + s.RenewEphemeralResourceCalled = make(map[string]bool) + } + + s.RenewEphemeralResourceCalled[req.TypeName] = true + return nil, nil +} + func (s *TestServer) StopProvider(_ context.Context, _ *tfprotov5.StopProviderRequest) (*tfprotov5.StopProviderResponse, error) { s.StopProviderCalled = true @@ -183,6 +218,15 @@ func (s *TestServer) UpgradeResourceState(_ context.Context, req *tfprotov5.Upgr return nil, nil } +func (s *TestServer) ValidateEphemeralResourceConfig(_ context.Context, req *tfprotov5.ValidateEphemeralResourceConfigRequest) (*tfprotov5.ValidateEphemeralResourceConfigResponse, error) { + if s.ValidateEphemeralResourceConfigCalled == nil { + s.ValidateEphemeralResourceConfigCalled = make(map[string]bool) + } + + s.ValidateEphemeralResourceConfigCalled[req.TypeName] = true + return nil, nil +} + func (s *TestServer) ValidateDataSourceConfig(_ context.Context, req *tfprotov5.ValidateDataSourceConfigRequest) (*tfprotov5.ValidateDataSourceConfigResponse, error) { if s.ValidateDataSourceConfigCalled == nil { s.ValidateDataSourceConfigCalled = make(map[string]bool) diff --git a/internal/tf6testserver/tf6testserver.go b/internal/tf6testserver/tf6testserver.go index 8687efd..3c605f0 100644 --- a/internal/tf6testserver/tf6testserver.go +++ b/internal/tf6testserver/tf6testserver.go @@ -18,6 +18,8 @@ type TestServer struct { CallFunctionCalled map[string]bool + CloseEphemeralResourceCalled map[string]bool + ConfigureProviderCalled bool ConfigureProviderResponse *tfprotov6.ConfigureProviderResponse @@ -34,12 +36,16 @@ type TestServer struct { MoveResourceStateCalled map[string]bool + OpenEphemeralResourceCalled map[string]bool + PlanResourceChangeCalled map[string]bool ReadDataSourceCalled map[string]bool ReadResourceCalled map[string]bool + RenewEphemeralResourceCalled map[string]bool + StopProviderCalled bool StopProviderResponse *tfprotov6.StopProviderResponse @@ -47,6 +53,8 @@ type TestServer struct { ValidateDataResourceConfigCalled map[string]bool + ValidateEphemeralResourceConfigCalled map[string]bool + ValidateProviderConfigCalled bool ValidateProviderConfigResponse *tfprotov6.ValidateProviderConfigResponse @@ -75,6 +83,15 @@ func (s *TestServer) CallFunction(_ context.Context, req *tfprotov6.CallFunction return nil, nil } +func (s *TestServer) CloseEphemeralResource(ctx context.Context, req *tfprotov6.CloseEphemeralResourceRequest) (*tfprotov6.CloseEphemeralResourceResponse, error) { + if s.CloseEphemeralResourceCalled == nil { + s.CloseEphemeralResourceCalled = make(map[string]bool) + } + + s.CloseEphemeralResourceCalled[req.TypeName] = true + return nil, nil +} + func (s *TestServer) ConfigureProvider(_ context.Context, _ *tfprotov6.ConfigureProviderRequest) (*tfprotov6.ConfigureProviderResponse, error) { s.ConfigureProviderCalled = true @@ -137,6 +154,15 @@ func (s *TestServer) MoveResourceState(_ context.Context, req *tfprotov6.MoveRes return nil, nil } +func (s *TestServer) OpenEphemeralResource(_ context.Context, req *tfprotov6.OpenEphemeralResourceRequest) (*tfprotov6.OpenEphemeralResourceResponse, error) { + if s.OpenEphemeralResourceCalled == nil { + s.OpenEphemeralResourceCalled = make(map[string]bool) + } + + s.OpenEphemeralResourceCalled[req.TypeName] = true + return nil, nil +} + func (s *TestServer) PlanResourceChange(_ context.Context, req *tfprotov6.PlanResourceChangeRequest) (*tfprotov6.PlanResourceChangeResponse, error) { if s.PlanResourceChangeCalled == nil { s.PlanResourceChangeCalled = make(map[string]bool) @@ -164,6 +190,15 @@ func (s *TestServer) ReadResource(_ context.Context, req *tfprotov6.ReadResource return nil, nil } +func (s *TestServer) RenewEphemeralResource(_ context.Context, req *tfprotov6.RenewEphemeralResourceRequest) (*tfprotov6.RenewEphemeralResourceResponse, error) { + if s.RenewEphemeralResourceCalled == nil { + s.RenewEphemeralResourceCalled = make(map[string]bool) + } + + s.RenewEphemeralResourceCalled[req.TypeName] = true + return nil, nil +} + func (s *TestServer) StopProvider(_ context.Context, _ *tfprotov6.StopProviderRequest) (*tfprotov6.StopProviderResponse, error) { s.StopProviderCalled = true @@ -183,6 +218,15 @@ func (s *TestServer) UpgradeResourceState(_ context.Context, req *tfprotov6.Upgr return nil, nil } +func (s *TestServer) ValidateEphemeralResourceConfig(_ context.Context, req *tfprotov6.ValidateEphemeralResourceConfigRequest) (*tfprotov6.ValidateEphemeralResourceConfigResponse, error) { + if s.ValidateEphemeralResourceConfigCalled == nil { + s.ValidateEphemeralResourceConfigCalled = make(map[string]bool) + } + + s.ValidateEphemeralResourceConfigCalled[req.TypeName] = true + return nil, nil +} + func (s *TestServer) ValidateDataResourceConfig(_ context.Context, req *tfprotov6.ValidateDataResourceConfigRequest) (*tfprotov6.ValidateDataResourceConfigResponse, error) { if s.ValidateDataResourceConfigCalled == nil { s.ValidateDataResourceConfigCalled = make(map[string]bool) diff --git a/internal/tfprotov5tov6/tfprotov5tov6.go b/internal/tfprotov5tov6/tfprotov5tov6.go index 3148b26..2c69b60 100644 --- a/internal/tfprotov5tov6/tfprotov5tov6.go +++ b/internal/tfprotov5tov6/tfprotov5tov6.go @@ -64,6 +64,28 @@ func CallFunctionResponse(in *tfprotov5.CallFunctionResponse) *tfprotov6.CallFun } } +func CloseEphemeralResourceRequest(in *tfprotov5.CloseEphemeralResourceRequest) *tfprotov6.CloseEphemeralResourceRequest { + if in == nil { + return nil + } + + return &tfprotov6.CloseEphemeralResourceRequest{ + TypeName: in.TypeName, + PriorState: DynamicValue(in.PriorState), + Private: in.Private, + } +} + +func CloseEphemeralResourceResponse(in *tfprotov5.CloseEphemeralResourceResponse) *tfprotov6.CloseEphemeralResourceResponse { + if in == nil { + return nil + } + + return &tfprotov6.CloseEphemeralResourceResponse{ + Diagnostics: Diagnostics(in.Diagnostics), + } +} + func ConfigureProviderRequest(in *tfprotov5.ConfigureProviderRequest) *tfprotov6.ConfigureProviderRequest { if in == nil { return nil @@ -151,6 +173,12 @@ func DynamicValue(in *tfprotov5.DynamicValue) *tfprotov6.DynamicValue { } } +func EphemeralResourceMetadata(in tfprotov5.EphemeralResourceMetadata) tfprotov6.EphemeralResourceMetadata { + return tfprotov6.EphemeralResourceMetadata{ + TypeName: in.TypeName, + } +} + func Function(in *tfprotov5.Function) *tfprotov6.Function { if in == nil { return nil @@ -258,6 +286,7 @@ func GetMetadataResponse(in *tfprotov5.GetMetadataResponse) *tfprotov6.GetMetada resp := &tfprotov6.GetMetadataResponse{ DataSources: make([]tfprotov6.DataSourceMetadata, 0, len(in.DataSources)), Diagnostics: Diagnostics(in.Diagnostics), + EphemeralResources: make([]tfprotov6.EphemeralResourceMetadata, 0, len(in.Resources)), Functions: make([]tfprotov6.FunctionMetadata, 0, len(in.Functions)), Resources: make([]tfprotov6.ResourceMetadata, 0, len(in.Resources)), ServerCapabilities: ServerCapabilities(in.ServerCapabilities), @@ -267,6 +296,10 @@ func GetMetadataResponse(in *tfprotov5.GetMetadataResponse) *tfprotov6.GetMetada resp.DataSources = append(resp.DataSources, DataSourceMetadata(datasource)) } + for _, ephemeralResource := range in.EphemeralResources { + resp.EphemeralResources = append(resp.EphemeralResources, EphemeralResourceMetadata(ephemeralResource)) + } + for _, function := range in.Functions { resp.Functions = append(resp.Functions, FunctionMetadata(function)) } @@ -297,6 +330,12 @@ func GetProviderSchemaResponse(in *tfprotov5.GetProviderSchemaResponse) *tfproto dataSourceSchemas[k] = Schema(v) } + ephemeralResourceSchemas := make(map[string]*tfprotov6.Schema, len(in.EphemeralResourceSchemas)) + + for k, v := range in.EphemeralResourceSchemas { + ephemeralResourceSchemas[k] = Schema(v) + } + functions := make(map[string]*tfprotov6.Function, len(in.Functions)) for name, function := range in.Functions { @@ -310,13 +349,14 @@ func GetProviderSchemaResponse(in *tfprotov5.GetProviderSchemaResponse) *tfproto } return &tfprotov6.GetProviderSchemaResponse{ - DataSourceSchemas: dataSourceSchemas, - Diagnostics: Diagnostics(in.Diagnostics), - Functions: functions, - Provider: Schema(in.Provider), - ProviderMeta: Schema(in.ProviderMeta), - ResourceSchemas: resourceSchemas, - ServerCapabilities: ServerCapabilities(in.ServerCapabilities), + DataSourceSchemas: dataSourceSchemas, + Diagnostics: Diagnostics(in.Diagnostics), + EphemeralResourceSchemas: ephemeralResourceSchemas, + Functions: functions, + Provider: Schema(in.Provider), + ProviderMeta: Schema(in.ProviderMeta), + ResourceSchemas: resourceSchemas, + ServerCapabilities: ServerCapabilities(in.ServerCapabilities), } } @@ -406,6 +446,31 @@ func MoveResourceStateResponse(in *tfprotov5.MoveResourceStateResponse) *tfproto } } +func OpenEphemeralResourceRequest(in *tfprotov5.OpenEphemeralResourceRequest) *tfprotov6.OpenEphemeralResourceRequest { + if in == nil { + return nil + } + + return &tfprotov6.OpenEphemeralResourceRequest{ + TypeName: in.TypeName, + Config: DynamicValue(in.Config), + } +} + +func OpenEphemeralResourceResponse(in *tfprotov5.OpenEphemeralResourceResponse) *tfprotov6.OpenEphemeralResourceResponse { + if in == nil { + return nil + } + + return &tfprotov6.OpenEphemeralResourceResponse{ + State: DynamicValue(in.State), + Diagnostics: Diagnostics(in.Diagnostics), + Private: in.Private, + RenewAt: in.RenewAt, + IsClosable: in.IsClosable, + } +} + func PlanResourceChangeRequest(in *tfprotov5.PlanResourceChangeRequest) *tfprotov6.PlanResourceChangeRequest { if in == nil { return nil @@ -535,6 +600,32 @@ func ReadResourceResponse(in *tfprotov5.ReadResourceResponse) *tfprotov6.ReadRes } } +func RenewEphemeralResourceRequest(in *tfprotov5.RenewEphemeralResourceRequest) *tfprotov6.RenewEphemeralResourceRequest { + if in == nil { + return nil + } + + return &tfprotov6.RenewEphemeralResourceRequest{ + TypeName: in.TypeName, + Config: DynamicValue(in.Config), + PriorState: DynamicValue(in.PriorState), + Private: in.Private, + } +} + +func RenewEphemeralResourceResponse(in *tfprotov5.RenewEphemeralResourceResponse) *tfprotov6.RenewEphemeralResourceResponse { + if in == nil { + return nil + } + + return &tfprotov6.RenewEphemeralResourceResponse{ + State: DynamicValue(in.State), + Diagnostics: Diagnostics(in.Diagnostics), + Private: in.Private, + RenewAt: in.RenewAt, + } +} + func ResourceMetadata(in tfprotov5.ResourceMetadata) tfprotov6.ResourceMetadata { return tfprotov6.ResourceMetadata{ TypeName: in.TypeName, @@ -675,6 +766,27 @@ func UpgradeResourceStateResponse(in *tfprotov5.UpgradeResourceStateResponse) *t } } +func ValidateEphemeralResourceConfigRequest(in *tfprotov5.ValidateEphemeralResourceConfigRequest) *tfprotov6.ValidateEphemeralResourceConfigRequest { + if in == nil { + return nil + } + + return &tfprotov6.ValidateEphemeralResourceConfigRequest{ + Config: DynamicValue(in.Config), + TypeName: in.TypeName, + } +} + +func ValidateEphemeralResourceConfigResponse(in *tfprotov5.ValidateEphemeralResourceConfigResponse) *tfprotov6.ValidateEphemeralResourceConfigResponse { + if in == nil { + return nil + } + + return &tfprotov6.ValidateEphemeralResourceConfigResponse{ + Diagnostics: Diagnostics(in.Diagnostics), + } +} + func ValidateDataResourceConfigRequest(in *tfprotov5.ValidateDataSourceConfigRequest) *tfprotov6.ValidateDataResourceConfigRequest { if in == nil { return nil diff --git a/internal/tfprotov5tov6/tfprotov5tov6_test.go b/internal/tfprotov5tov6/tfprotov5tov6_test.go index 1e15d90..71ab38d 100644 --- a/internal/tfprotov5tov6/tfprotov5tov6_test.go +++ b/internal/tfprotov5tov6/tfprotov5tov6_test.go @@ -5,6 +5,7 @@ package tfprotov5tov6_test import ( "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-go/tfprotov5" @@ -43,6 +44,14 @@ var ( testTfprotov5DynamicValue tfprotov5.DynamicValue testTfprotov6DynamicValue tfprotov6.DynamicValue + testTfprotov5EphemeralResourceMetadata tfprotov5.EphemeralResourceMetadata = tfprotov5.EphemeralResourceMetadata{ + TypeName: "test_ephemeral_resource", + } + + testTfprotov6EphemeralResourceMetadata tfprotov6.EphemeralResourceMetadata = tfprotov6.EphemeralResourceMetadata{ + TypeName: "test_ephemeral_resource", + } + testTfprotov5Function *tfprotov5.Function = &tfprotov5.Function{ Parameters: []*tfprotov5.FunctionParameter{}, Return: &tfprotov5.FunctionReturn{ @@ -107,6 +116,8 @@ var ( }, Version: 1, } + + testTime time.Time = time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC) ) func init() { @@ -282,6 +293,82 @@ func TestCallFunctionResponse(t *testing.T) { } } +func TestCloseEphemeralResourceRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov5.CloseEphemeralResourceRequest + expected *tfprotov6.CloseEphemeralResourceRequest + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov5.CloseEphemeralResourceRequest{ + PriorState: &testTfprotov5DynamicValue, + Private: testBytes, + TypeName: "test_ephemeral_resource", + }, + expected: &tfprotov6.CloseEphemeralResourceRequest{ + PriorState: &testTfprotov6DynamicValue, + Private: testBytes, + TypeName: "test_ephemeral_resource", + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov5tov6.CloseEphemeralResourceRequest(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCloseEphemeralResourceResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov5.CloseEphemeralResourceResponse + expected *tfprotov6.CloseEphemeralResourceResponse + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov5.CloseEphemeralResourceResponse{ + Diagnostics: testTfprotov5Diagnostics, + }, + expected: &tfprotov6.CloseEphemeralResourceResponse{ + Diagnostics: testTfprotov6Diagnostics, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov5tov6.CloseEphemeralResourceResponse(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestConfigureProviderRequest(t *testing.T) { t.Parallel() @@ -767,6 +854,9 @@ func TestGetMetadataResponse(t *testing.T) { testTfprotov5DataSourceMetadata, }, Diagnostics: testTfprotov5Diagnostics, + EphemeralResources: []tfprotov5.EphemeralResourceMetadata{ + testTfprotov5EphemeralResourceMetadata, + }, Functions: []tfprotov5.FunctionMetadata{ testTfprotov5FunctionMetadata, }, @@ -779,6 +869,9 @@ func TestGetMetadataResponse(t *testing.T) { testTfprotov6DataSourceMetadata, }, Diagnostics: testTfprotov6Diagnostics, + EphemeralResources: []tfprotov6.EphemeralResourceMetadata{ + testTfprotov6EphemeralResourceMetadata, + }, Functions: []tfprotov6.FunctionMetadata{ testTfprotov6FunctionMetadata, }, @@ -853,6 +946,9 @@ func TestGetProviderSchemaResponse(t *testing.T) { "test_data_source": testTfprotov5Schema, }, Diagnostics: testTfprotov5Diagnostics, + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource": testTfprotov5Schema, + }, Functions: map[string]*tfprotov5.Function{ "test_function": testTfprotov5Function, }, @@ -867,6 +963,9 @@ func TestGetProviderSchemaResponse(t *testing.T) { "test_data_source": testTfprotov6Schema, }, Diagnostics: testTfprotov6Diagnostics, + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource": testTfprotov6Schema, + }, Functions: map[string]*tfprotov6.Function{ "test_function": testTfprotov6Function, }, @@ -1191,6 +1290,88 @@ func TestMoveResourceStateResponse(t *testing.T) { } } +func TestOpenEphemeralResourceRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov5.OpenEphemeralResourceRequest + expected *tfprotov6.OpenEphemeralResourceRequest + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov5.OpenEphemeralResourceRequest{ + Config: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", + }, + expected: &tfprotov6.OpenEphemeralResourceRequest{ + Config: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov5tov6.OpenEphemeralResourceRequest(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestOpenEphemeralResourceResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov5.OpenEphemeralResourceResponse + expected *tfprotov6.OpenEphemeralResourceResponse + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov5.OpenEphemeralResourceResponse{ + Diagnostics: testTfprotov5Diagnostics, + IsClosable: true, + Private: testBytes, + RenewAt: testTime, + State: &testTfprotov5DynamicValue, + }, + expected: &tfprotov6.OpenEphemeralResourceResponse{ + Diagnostics: testTfprotov6Diagnostics, + IsClosable: true, + Private: testBytes, + RenewAt: testTime, + State: &testTfprotov6DynamicValue, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov5tov6.OpenEphemeralResourceResponse(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestPlanResourceChangeRequest(t *testing.T) { t.Parallel() @@ -1605,6 +1786,88 @@ func TestReadResourceResponse(t *testing.T) { } } +func TestRenewEphemeralResourceRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov5.RenewEphemeralResourceRequest + expected *tfprotov6.RenewEphemeralResourceRequest + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov5.RenewEphemeralResourceRequest{ + Config: &testTfprotov5DynamicValue, + PriorState: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", + }, + expected: &tfprotov6.RenewEphemeralResourceRequest{ + Config: &testTfprotov6DynamicValue, + PriorState: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov5tov6.RenewEphemeralResourceRequest(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestRenewEphemeralResourceResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov5.RenewEphemeralResourceResponse + expected *tfprotov6.RenewEphemeralResourceResponse + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov5.RenewEphemeralResourceResponse{ + Diagnostics: testTfprotov5Diagnostics, + Private: testBytes, + RenewAt: testTime, + State: &testTfprotov5DynamicValue, + }, + expected: &tfprotov6.RenewEphemeralResourceResponse{ + Diagnostics: testTfprotov6Diagnostics, + Private: testBytes, + RenewAt: testTime, + State: &testTfprotov6DynamicValue, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov5tov6.RenewEphemeralResourceResponse(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSchema(t *testing.T) { t.Parallel() @@ -2083,6 +2346,80 @@ func TestValidateDataResourceConfigResponse(t *testing.T) { } } +func TestValidateEphemeralResourceConfigRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov5.ValidateEphemeralResourceConfigRequest + expected *tfprotov6.ValidateEphemeralResourceConfigRequest + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov5.ValidateEphemeralResourceConfigRequest{ + Config: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", + }, + expected: &tfprotov6.ValidateEphemeralResourceConfigRequest{ + Config: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov5tov6.ValidateEphemeralResourceConfigRequest(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestValidateEphemeralResourceConfigResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov5.ValidateEphemeralResourceConfigResponse + expected *tfprotov6.ValidateEphemeralResourceConfigResponse + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov5.ValidateEphemeralResourceConfigResponse{ + Diagnostics: testTfprotov5Diagnostics, + }, + expected: &tfprotov6.ValidateEphemeralResourceConfigResponse{ + Diagnostics: testTfprotov6Diagnostics, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov5tov6.ValidateEphemeralResourceConfigResponse(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestValidateProviderConfigRequest(t *testing.T) { t.Parallel() diff --git a/internal/tfprotov6tov5/tfprotov6tov5.go b/internal/tfprotov6tov5/tfprotov6tov5.go index 24846dc..21eb2dc 100644 --- a/internal/tfprotov6tov5/tfprotov6tov5.go +++ b/internal/tfprotov6tov5/tfprotov6tov5.go @@ -69,6 +69,28 @@ func CallFunctionResponse(in *tfprotov6.CallFunctionResponse) *tfprotov5.CallFun } } +func CloseEphemeralResourceRequest(in *tfprotov6.CloseEphemeralResourceRequest) *tfprotov5.CloseEphemeralResourceRequest { + if in == nil { + return nil + } + + return &tfprotov5.CloseEphemeralResourceRequest{ + TypeName: in.TypeName, + PriorState: DynamicValue(in.PriorState), + Private: in.Private, + } +} + +func CloseEphemeralResourceResponse(in *tfprotov6.CloseEphemeralResourceResponse) *tfprotov5.CloseEphemeralResourceResponse { + if in == nil { + return nil + } + + return &tfprotov5.CloseEphemeralResourceResponse{ + Diagnostics: Diagnostics(in.Diagnostics), + } +} + func ConfigureProviderRequest(in *tfprotov6.ConfigureProviderRequest) *tfprotov5.ConfigureProviderRequest { if in == nil { return nil @@ -156,6 +178,12 @@ func DynamicValue(in *tfprotov6.DynamicValue) *tfprotov5.DynamicValue { } } +func EphemeralResourceMetadata(in tfprotov6.EphemeralResourceMetadata) tfprotov5.EphemeralResourceMetadata { + return tfprotov5.EphemeralResourceMetadata{ + TypeName: in.TypeName, + } +} + func Function(in *tfprotov6.Function) *tfprotov5.Function { if in == nil { return nil @@ -263,6 +291,7 @@ func GetMetadataResponse(in *tfprotov6.GetMetadataResponse) *tfprotov5.GetMetada resp := &tfprotov5.GetMetadataResponse{ DataSources: make([]tfprotov5.DataSourceMetadata, 0, len(in.DataSources)), Diagnostics: Diagnostics(in.Diagnostics), + EphemeralResources: make([]tfprotov5.EphemeralResourceMetadata, 0, len(in.Resources)), Functions: make([]tfprotov5.FunctionMetadata, 0, len(in.Functions)), Resources: make([]tfprotov5.ResourceMetadata, 0, len(in.Resources)), ServerCapabilities: ServerCapabilities(in.ServerCapabilities), @@ -272,6 +301,10 @@ func GetMetadataResponse(in *tfprotov6.GetMetadataResponse) *tfprotov5.GetMetada resp.DataSources = append(resp.DataSources, DataSourceMetadata(datasource)) } + for _, ephemeralResource := range in.EphemeralResources { + resp.EphemeralResources = append(resp.EphemeralResources, EphemeralResourceMetadata(ephemeralResource)) + } + for _, function := range in.Functions { resp.Functions = append(resp.Functions, FunctionMetadata(function)) } @@ -308,6 +341,18 @@ func GetProviderSchemaResponse(in *tfprotov6.GetProviderSchemaResponse) (*tfprot dataSourceSchemas[k] = v5Schema } + ephemeralResourceSchemas := make(map[string]*tfprotov5.Schema, len(in.EphemeralResourceSchemas)) + + for k, v := range in.EphemeralResourceSchemas { + v5Schema, err := Schema(v) + + if err != nil { + return nil, fmt.Errorf("unable to convert ephemeral resource %q schema: %w", k, err) + } + + ephemeralResourceSchemas[k] = v5Schema + } + functions := make(map[string]*tfprotov5.Function, len(in.Functions)) for name, function := range in.Functions { @@ -339,12 +384,13 @@ func GetProviderSchemaResponse(in *tfprotov6.GetProviderSchemaResponse) (*tfprot } return &tfprotov5.GetProviderSchemaResponse{ - DataSourceSchemas: dataSourceSchemas, - Diagnostics: Diagnostics(in.Diagnostics), - Functions: functions, - Provider: provider, - ProviderMeta: providerMeta, - ResourceSchemas: resourceSchemas, + DataSourceSchemas: dataSourceSchemas, + Diagnostics: Diagnostics(in.Diagnostics), + EphemeralResourceSchemas: ephemeralResourceSchemas, + Functions: functions, + Provider: provider, + ProviderMeta: providerMeta, + ResourceSchemas: resourceSchemas, }, nil } @@ -434,6 +480,31 @@ func MoveResourceStateResponse(in *tfprotov6.MoveResourceStateResponse) *tfproto } } +func OpenEphemeralResourceRequest(in *tfprotov6.OpenEphemeralResourceRequest) *tfprotov5.OpenEphemeralResourceRequest { + if in == nil { + return nil + } + + return &tfprotov5.OpenEphemeralResourceRequest{ + TypeName: in.TypeName, + Config: DynamicValue(in.Config), + } +} + +func OpenEphemeralResourceResponse(in *tfprotov6.OpenEphemeralResourceResponse) *tfprotov5.OpenEphemeralResourceResponse { + if in == nil { + return nil + } + + return &tfprotov5.OpenEphemeralResourceResponse{ + State: DynamicValue(in.State), + Diagnostics: Diagnostics(in.Diagnostics), + Private: in.Private, + RenewAt: in.RenewAt, + IsClosable: in.IsClosable, + } +} + func PlanResourceChangeRequest(in *tfprotov6.PlanResourceChangeRequest) *tfprotov5.PlanResourceChangeRequest { if in == nil { return nil @@ -583,6 +654,32 @@ func ReadResourceResponse(in *tfprotov6.ReadResourceResponse) *tfprotov5.ReadRes } } +func RenewEphemeralResourceRequest(in *tfprotov6.RenewEphemeralResourceRequest) *tfprotov5.RenewEphemeralResourceRequest { + if in == nil { + return nil + } + + return &tfprotov5.RenewEphemeralResourceRequest{ + TypeName: in.TypeName, + Config: DynamicValue(in.Config), + PriorState: DynamicValue(in.PriorState), + Private: in.Private, + } +} + +func RenewEphemeralResourceResponse(in *tfprotov6.RenewEphemeralResourceResponse) *tfprotov5.RenewEphemeralResourceResponse { + if in == nil { + return nil + } + + return &tfprotov5.RenewEphemeralResourceResponse{ + State: DynamicValue(in.State), + Diagnostics: Diagnostics(in.Diagnostics), + Private: in.Private, + RenewAt: in.RenewAt, + } +} + func ResourceMetadata(in tfprotov6.ResourceMetadata) tfprotov5.ResourceMetadata { return tfprotov5.ResourceMetadata{ TypeName: in.TypeName, @@ -772,6 +869,27 @@ func ValidateDataSourceConfigResponse(in *tfprotov6.ValidateDataResourceConfigRe } } +func ValidateEphemeralResourceConfigRequest(in *tfprotov6.ValidateEphemeralResourceConfigRequest) *tfprotov5.ValidateEphemeralResourceConfigRequest { + if in == nil { + return nil + } + + return &tfprotov5.ValidateEphemeralResourceConfigRequest{ + Config: DynamicValue(in.Config), + TypeName: in.TypeName, + } +} + +func ValidateEphemeralResourceConfigResponse(in *tfprotov6.ValidateEphemeralResourceConfigResponse) *tfprotov5.ValidateEphemeralResourceConfigResponse { + if in == nil { + return nil + } + + return &tfprotov5.ValidateEphemeralResourceConfigResponse{ + Diagnostics: Diagnostics(in.Diagnostics), + } +} + func ValidateResourceTypeConfigRequest(in *tfprotov6.ValidateResourceConfigRequest) *tfprotov5.ValidateResourceTypeConfigRequest { if in == nil { return nil diff --git a/internal/tfprotov6tov5/tfprotov6tov5_test.go b/internal/tfprotov6tov5/tfprotov6tov5_test.go index 9252d42..3a3eae1 100644 --- a/internal/tfprotov6tov5/tfprotov6tov5_test.go +++ b/internal/tfprotov6tov5/tfprotov6tov5_test.go @@ -7,6 +7,7 @@ import ( "fmt" "strings" "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-go/tfprotov5" @@ -45,6 +46,14 @@ var ( testTfprotov5DynamicValue tfprotov5.DynamicValue testTfprotov6DynamicValue tfprotov6.DynamicValue + testTfprotov5EphemeralResourceMetadata tfprotov5.EphemeralResourceMetadata = tfprotov5.EphemeralResourceMetadata{ + TypeName: "test_ephemeral_resource", + } + + testTfprotov6EphemeralResourceMetadata tfprotov6.EphemeralResourceMetadata = tfprotov6.EphemeralResourceMetadata{ + TypeName: "test_ephemeral_resource", + } + testTfprotov5Function *tfprotov5.Function = &tfprotov5.Function{ Parameters: []*tfprotov5.FunctionParameter{}, Return: &tfprotov5.FunctionReturn{ @@ -109,6 +118,8 @@ var ( }, Version: 1, } + + testTime time.Time = time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC) ) func init() { @@ -284,6 +295,82 @@ func TestCallFunctionResponse(t *testing.T) { } } +func TestCloseEphemeralResourceRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov6.CloseEphemeralResourceRequest + expected *tfprotov5.CloseEphemeralResourceRequest + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov6.CloseEphemeralResourceRequest{ + PriorState: &testTfprotov6DynamicValue, + Private: testBytes, + TypeName: "test_ephemeral_resource", + }, + expected: &tfprotov5.CloseEphemeralResourceRequest{ + PriorState: &testTfprotov5DynamicValue, + Private: testBytes, + TypeName: "test_ephemeral_resource", + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov6tov5.CloseEphemeralResourceRequest(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestCloseEphemeralResourceResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov6.CloseEphemeralResourceResponse + expected *tfprotov5.CloseEphemeralResourceResponse + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov6.CloseEphemeralResourceResponse{ + Diagnostics: testTfprotov6Diagnostics, + }, + expected: &tfprotov5.CloseEphemeralResourceResponse{ + Diagnostics: testTfprotov5Diagnostics, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov6tov5.CloseEphemeralResourceResponse(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestConfigureProviderRequest(t *testing.T) { t.Parallel() @@ -769,6 +856,9 @@ func TestGetMetadataResponse(t *testing.T) { testTfprotov6DataSourceMetadata, }, Diagnostics: testTfprotov6Diagnostics, + EphemeralResources: []tfprotov6.EphemeralResourceMetadata{ + testTfprotov6EphemeralResourceMetadata, + }, Functions: []tfprotov6.FunctionMetadata{ testTfprotov6FunctionMetadata, }, @@ -781,6 +871,9 @@ func TestGetMetadataResponse(t *testing.T) { testTfprotov5DataSourceMetadata, }, Diagnostics: testTfprotov5Diagnostics, + EphemeralResources: []tfprotov5.EphemeralResourceMetadata{ + testTfprotov5EphemeralResourceMetadata, + }, Functions: []tfprotov5.FunctionMetadata{ testTfprotov5FunctionMetadata, }, @@ -856,6 +949,9 @@ func TestGetProviderSchemaResponse(t *testing.T) { "test_data_source": testTfprotov6Schema, }, Diagnostics: testTfprotov6Diagnostics, + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource": testTfprotov6Schema, + }, Functions: map[string]*tfprotov6.Function{ "test_function": testTfprotov6Function, }, @@ -870,6 +966,9 @@ func TestGetProviderSchemaResponse(t *testing.T) { "test_data_source": testTfprotov5Schema, }, Diagnostics: testTfprotov5Diagnostics, + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource": testTfprotov5Schema, + }, Functions: map[string]*tfprotov5.Function{ "test_function": testTfprotov5Function, }, @@ -906,6 +1005,32 @@ func TestGetProviderSchemaResponse(t *testing.T) { expected: nil, expectedError: fmt.Errorf("unable to convert data source \"test_data_source\" schema: unable to convert attribute \"test_attribute\" schema: %w", tfprotov6tov5.ErrSchemaAttributeNestedTypeNotImplemented), }, + "ephemeral-resource-nested-attribute-error": { + in: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource": { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_attribute", + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_nested_attribute", + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + }, + }, + }, + expected: nil, + expectedError: fmt.Errorf("unable to convert ephemeral resource \"test_ephemeral_resource\" schema: unable to convert attribute \"test_attribute\" schema: %w", tfprotov6tov5.ErrSchemaAttributeNestedTypeNotImplemented), + }, "provider-nested-attribute-error": { in: &tfprotov6.GetProviderSchemaResponse{ Provider: &tfprotov6.Schema{ @@ -1306,6 +1431,88 @@ func TestMoveResourceStateResponse(t *testing.T) { } } +func TestOpenEphemeralResourceRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov6.OpenEphemeralResourceRequest + expected *tfprotov5.OpenEphemeralResourceRequest + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov6.OpenEphemeralResourceRequest{ + Config: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", + }, + expected: &tfprotov5.OpenEphemeralResourceRequest{ + Config: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov6tov5.OpenEphemeralResourceRequest(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestOpenEphemeralResourceResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov6.OpenEphemeralResourceResponse + expected *tfprotov5.OpenEphemeralResourceResponse + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov6.OpenEphemeralResourceResponse{ + Diagnostics: testTfprotov6Diagnostics, + IsClosable: true, + Private: testBytes, + RenewAt: testTime, + State: &testTfprotov6DynamicValue, + }, + expected: &tfprotov5.OpenEphemeralResourceResponse{ + Diagnostics: testTfprotov5Diagnostics, + IsClosable: true, + Private: testBytes, + RenewAt: testTime, + State: &testTfprotov5DynamicValue, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov6tov5.OpenEphemeralResourceResponse(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestPlanResourceChangeRequest(t *testing.T) { t.Parallel() @@ -1794,6 +2001,88 @@ func TestReadResourceResponse(t *testing.T) { } } +func TestRenewEphemeralResourceRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov6.RenewEphemeralResourceRequest + expected *tfprotov5.RenewEphemeralResourceRequest + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov6.RenewEphemeralResourceRequest{ + Config: &testTfprotov6DynamicValue, + PriorState: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", + }, + expected: &tfprotov5.RenewEphemeralResourceRequest{ + Config: &testTfprotov5DynamicValue, + PriorState: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov6tov5.RenewEphemeralResourceRequest(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestRenewEphemeralResourceResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov6.RenewEphemeralResourceResponse + expected *tfprotov5.RenewEphemeralResourceResponse + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov6.RenewEphemeralResourceResponse{ + Diagnostics: testTfprotov6Diagnostics, + Private: testBytes, + RenewAt: testTime, + State: &testTfprotov6DynamicValue, + }, + expected: &tfprotov5.RenewEphemeralResourceResponse{ + Diagnostics: testTfprotov5Diagnostics, + Private: testBytes, + RenewAt: testTime, + State: &testTfprotov5DynamicValue, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov6tov5.RenewEphemeralResourceResponse(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSchema(t *testing.T) { t.Parallel() @@ -2406,6 +2695,80 @@ func TestValidateDataSourceConfigResponse(t *testing.T) { } } +func TestValidateEphemeralResourceConfigRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov6.ValidateEphemeralResourceConfigRequest + expected *tfprotov5.ValidateEphemeralResourceConfigRequest + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov6.ValidateEphemeralResourceConfigRequest{ + Config: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", + }, + expected: &tfprotov5.ValidateEphemeralResourceConfigRequest{ + Config: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov6tov5.ValidateEphemeralResourceConfigRequest(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestValidateEphemeralResourceConfigResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in *tfprotov6.ValidateEphemeralResourceConfigResponse + expected *tfprotov5.ValidateEphemeralResourceConfigResponse + }{ + "nil": { + in: nil, + expected: nil, + }, + "all-valid-fields": { + in: &tfprotov6.ValidateEphemeralResourceConfigResponse{ + Diagnostics: testTfprotov6Diagnostics, + }, + expected: &tfprotov5.ValidateEphemeralResourceConfigResponse{ + Diagnostics: testTfprotov5Diagnostics, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := tfprotov6tov5.ValidateEphemeralResourceConfigResponse(testCase.in) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestValidateResourceTypeConfigRequest(t *testing.T) { t.Parallel() diff --git a/tf5muxserver/diagnostics.go b/tf5muxserver/diagnostics.go index 4348d23..e0761ff 100644 --- a/tf5muxserver/diagnostics.go +++ b/tf5muxserver/diagnostics.go @@ -26,6 +26,27 @@ func dataSourceMissingError(typeName string) *tfprotov5.Diagnostic { } } +func ephemeralResourceDuplicateError(typeName string) *tfprotov5.Diagnostic { + return &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Invalid Provider Server Combination", + Detail: "The combined provider has multiple implementations of the same ephemeral resource type across underlying providers. " + + "Ephemeral resource types must be implemented by only one underlying provider. " + + "This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" + + "Duplicate ephemeral resource type: " + typeName, + } +} + +func ephemeralResourceMissingError(typeName string) *tfprotov5.Diagnostic { + return &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Ephemeral Resource Not Implemented", + Detail: "The combined provider does not implement the requested ephemeral resource type. " + + "This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" + + "Missing ephemeral resource type: " + typeName, + } +} + func diagnosticsHasError(diagnostics []*tfprotov5.Diagnostic) bool { for _, diagnostic := range diagnostics { if diagnostic == nil { diff --git a/tf5muxserver/mux_server.go b/tf5muxserver/mux_server.go index 91137db..ba23971 100644 --- a/tf5muxserver/mux_server.go +++ b/tf5muxserver/mux_server.go @@ -8,9 +8,10 @@ import ( "sync" "github.com/hashicorp/terraform-plugin-go/tfprotov5" - "github.com/hashicorp/terraform-plugin-mux/internal/logging" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/hashicorp/terraform-plugin-mux/internal/logging" ) var _ tfprotov5.ProviderServer = &muxServer{} @@ -22,6 +23,9 @@ type muxServer struct { // Routing for data source types dataSources map[string]tfprotov5.ProviderServer + // Routing for ephemeral resource types + ephemeralResources map[string]tfprotov5.ProviderServer + // Routing for functions functions map[string]tfprotov5.ProviderServer @@ -90,6 +94,41 @@ func (s *muxServer) getDataSourceServer(ctx context.Context, typeName string) (t return server, s.serverDiscoveryDiagnostics, nil } +func (s *muxServer) getEphemeralResourceServer(ctx context.Context, typeName string) (tfprotov5.ProviderServer, []*tfprotov5.Diagnostic, error) { + s.serverDiscoveryMutex.RLock() + server, ok := s.ephemeralResources[typeName] + discoveryComplete := s.serverDiscoveryComplete + s.serverDiscoveryMutex.RUnlock() + + if discoveryComplete { + if ok { + return server, s.serverDiscoveryDiagnostics, nil + } + + return nil, []*tfprotov5.Diagnostic{ + ephemeralResourceMissingError(typeName), + }, nil + } + + err := s.serverDiscovery(ctx) + + if err != nil || diagnosticsHasError(s.serverDiscoveryDiagnostics) { + return nil, s.serverDiscoveryDiagnostics, err + } + + s.serverDiscoveryMutex.RLock() + server, ok = s.ephemeralResources[typeName] + s.serverDiscoveryMutex.RUnlock() + + if !ok { + return nil, []*tfprotov5.Diagnostic{ + ephemeralResourceMissingError(typeName), + }, nil + } + + return server, s.serverDiscoveryDiagnostics, nil +} + func (s *muxServer) getFunctionServer(ctx context.Context, name string) (tfprotov5.ProviderServer, []*tfprotov5.Diagnostic, error) { s.serverDiscoveryMutex.RLock() server, ok := s.functions[name] @@ -163,7 +202,7 @@ func (s *muxServer) getResourceServer(ctx context.Context, typeName string) (tfp // serverDiscovery will populate the mux server "routing" for functions and // resource types by calling all underlying server GetMetadata RPC and falling // back to GetProviderSchema RPC. It is intended to only be called through -// getDataSourceServer, getFunctionServer, and getResourceServer. +// getDataSourceServer, getEphemeralResourceServer, getFunctionServer, and getResourceServer. // // The error return represents gRPC errors, which except for the GetMetadata // call returning the gRPC unimplemented error, is always returned. @@ -201,6 +240,16 @@ func (s *muxServer) serverDiscovery(ctx context.Context) error { s.dataSources[serverDataSource.TypeName] = server } + for _, serverEphemeralResource := range metadataResp.EphemeralResources { + if _, ok := s.ephemeralResources[serverEphemeralResource.TypeName]; ok { + s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, ephemeralResourceDuplicateError(serverEphemeralResource.TypeName)) + + continue + } + + s.ephemeralResources[serverEphemeralResource.TypeName] = server + } + for _, serverFunction := range metadataResp.Functions { if _, ok := s.functions[serverFunction.Name]; ok { s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, functionDuplicateError(serverFunction.Name)) @@ -253,6 +302,16 @@ func (s *muxServer) serverDiscovery(ctx context.Context) error { s.dataSources[typeName] = server } + for typeName := range providerSchemaResp.EphemeralResourceSchemas { + if _, ok := s.ephemeralResources[typeName]; ok { + s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, ephemeralResourceDuplicateError(typeName)) + + continue + } + + s.ephemeralResources[typeName] = server + } + for name := range providerSchemaResp.Functions { if _, ok := s.functions[name]; ok { s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, functionDuplicateError(name)) @@ -289,9 +348,11 @@ func (s *muxServer) serverDiscovery(ctx context.Context) error { // - Only one provider implements each managed resource // - Only one provider implements each data source // - Only one provider implements each function +// - Only one provider implements each ephemeral resource func NewMuxServer(_ context.Context, servers ...func() tfprotov5.ProviderServer) (*muxServer, error) { result := muxServer{ dataSources: make(map[string]tfprotov5.ProviderServer), + ephemeralResources: make(map[string]tfprotov5.ProviderServer), functions: make(map[string]tfprotov5.ProviderServer), resources: make(map[string]tfprotov5.ProviderServer), resourceCapabilities: make(map[string]*tfprotov5.ServerCapabilities), diff --git a/tf5muxserver/mux_server_CloseEphemeralResource.go b/tf5muxserver/mux_server_CloseEphemeralResource.go new file mode 100644 index 0000000..e711a55 --- /dev/null +++ b/tf5muxserver/mux_server_CloseEphemeralResource.go @@ -0,0 +1,35 @@ +// 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) CloseEphemeralResource(ctx context.Context, req *tfprotov5.CloseEphemeralResourceRequest) (*tfprotov5.CloseEphemeralResourceResponse, error) { + rpc := "CloseEphemeralResource" + ctx = logging.InitContext(ctx) + ctx = logging.RpcContext(ctx, rpc) + + server, diags, err := s.getEphemeralResourceServer(ctx, req.TypeName) + + if err != nil { + return nil, err + } + + if diagnosticsHasError(diags) { + return &tfprotov5.CloseEphemeralResourceResponse{ + Diagnostics: diags, + }, nil + } + + ctx = logging.Tfprotov5ProviderServerContext(ctx, server) + logging.MuxTrace(ctx, "calling downstream server") + + return server.CloseEphemeralResource(ctx, req) +} diff --git a/tf5muxserver/mux_server_CloseEphemeralResource_test.go b/tf5muxserver/mux_server_CloseEphemeralResource_test.go new file mode 100644 index 0000000..5fe8049 --- /dev/null +++ b/tf5muxserver/mux_server_CloseEphemeralResource_test.go @@ -0,0 +1,72 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf5muxserver_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + + "github.com/hashicorp/terraform-plugin-mux/internal/tf5testserver" + "github.com/hashicorp/terraform-plugin-mux/tf5muxserver" +) + +func TestMuxServerCloseEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testServer1 := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource_server1": {}, + }, + }, + } + testServer2 := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource_server2": {}, + }, + }, + } + servers := []func() tfprotov5.ProviderServer{testServer1.ProviderServer, testServer2.ProviderServer} + muxServer, err := tf5muxserver.NewMuxServer(ctx, servers...) + + if err != nil { + t.Fatalf("unexpected error setting up factory: %s", err) + } + + _, err = muxServer.ProviderServer().CloseEphemeralResource(ctx, &tfprotov5.CloseEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server1", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !testServer1.CloseEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("expected test_ephemeral_resource_server1 CloseEphemeralResource to be called on server1") + } + + if testServer2.CloseEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("unexpected test_ephemeral_resource_server1 CloseEphemeralResource called on server2") + } + + _, err = muxServer.ProviderServer().CloseEphemeralResource(ctx, &tfprotov5.CloseEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server2", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if testServer1.CloseEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("unexpected test_ephemeral_resource_server2 CloseEphemeralResource called on server1") + } + + if !testServer2.CloseEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("expected test_ephemeral_resource_server2 CloseEphemeralResource to be called on server2") + } +} diff --git a/tf5muxserver/mux_server_GetMetadata.go b/tf5muxserver/mux_server_GetMetadata.go index bd3e1b4..4e57e66 100644 --- a/tf5muxserver/mux_server_GetMetadata.go +++ b/tf5muxserver/mux_server_GetMetadata.go @@ -14,8 +14,8 @@ import ( // GetMetadata merges the metadata returned by the // tfprotov5.ProviderServers associated with muxServer into a single response. -// Resources and data sources must be returned from only one server or an error -// diagnostic is returned. +// Resources, data sources, ephemeral resources, and functions must be returned +// from only one server or an error diagnostic is returned. func (s *muxServer) GetMetadata(ctx context.Context, req *tfprotov5.GetMetadataRequest) (*tfprotov5.GetMetadataResponse, error) { rpc := "GetMetadata" ctx = logging.InitContext(ctx) @@ -26,6 +26,7 @@ func (s *muxServer) GetMetadata(ctx context.Context, req *tfprotov5.GetMetadataR resp := &tfprotov5.GetMetadataResponse{ DataSources: make([]tfprotov5.DataSourceMetadata, 0), + EphemeralResources: make([]tfprotov5.EphemeralResourceMetadata, 0), Functions: make([]tfprotov5.FunctionMetadata, 0), Resources: make([]tfprotov5.ResourceMetadata, 0), ServerCapabilities: serverCapabilities, @@ -54,6 +55,17 @@ func (s *muxServer) GetMetadata(ctx context.Context, req *tfprotov5.GetMetadataR resp.DataSources = append(resp.DataSources, datasource) } + for _, ephemeralResource := range serverResp.EphemeralResources { + if ephemeralResourceMetadataContainsTypeName(resp.EphemeralResources, ephemeralResource.TypeName) { + resp.Diagnostics = append(resp.Diagnostics, ephemeralResourceDuplicateError(ephemeralResource.TypeName)) + + continue + } + + s.ephemeralResources[ephemeralResource.TypeName] = server + resp.EphemeralResources = append(resp.EphemeralResources, ephemeralResource) + } + for _, function := range serverResp.Functions { if functionMetadataContainsName(resp.Functions, function.Name) { resp.Diagnostics = append(resp.Diagnostics, functionDuplicateError(function.Name)) @@ -91,6 +103,16 @@ func datasourceMetadataContainsTypeName(metadatas []tfprotov5.DataSourceMetadata return false } +func ephemeralResourceMetadataContainsTypeName(metadatas []tfprotov5.EphemeralResourceMetadata, typeName string) bool { + for _, metadata := range metadatas { + if typeName == metadata.TypeName { + return true + } + } + + return false +} + func functionMetadataContainsName(metadatas []tfprotov5.FunctionMetadata, name string) bool { for _, metadata := range metadatas { if name == metadata.Name { diff --git a/tf5muxserver/mux_server_GetMetadata_test.go b/tf5muxserver/mux_server_GetMetadata_test.go index 1f633e5..ac66e5d 100644 --- a/tf5muxserver/mux_server_GetMetadata_test.go +++ b/tf5muxserver/mux_server_GetMetadata_test.go @@ -21,6 +21,7 @@ func TestMuxServerGetMetadata(t *testing.T) { servers []func() tfprotov5.ProviderServer expectedDataSources []tfprotov5.DataSourceMetadata expectedDiagnostics []*tfprotov5.Diagnostic + expectedEphemeralResources []tfprotov5.EphemeralResourceMetadata expectedFunctions []tfprotov5.FunctionMetadata expectedResources []tfprotov5.ResourceMetadata expectedServerCapabilities *tfprotov5.ServerCapabilities @@ -47,6 +48,14 @@ func TestMuxServerGetMetadata(t *testing.T) { Name: "test_function1", }, }, + EphemeralResources: []tfprotov5.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + { + TypeName: "test_bar", + }, + }, }, }).ProviderServer, (&tf5testserver.TestServer{ @@ -72,6 +81,11 @@ func TestMuxServerGetMetadata(t *testing.T) { Name: "test_function3", }, }, + EphemeralResources: []tfprotov5.EphemeralResourceMetadata{ + { + TypeName: "test_quux", + }, + }, }, }).ProviderServer, }, @@ -108,6 +122,17 @@ func TestMuxServerGetMetadata(t *testing.T) { Name: "test_function3", }, }, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + { + TypeName: "test_bar", + }, + { + TypeName: "test_quux", + }, + }, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -150,6 +175,52 @@ func TestMuxServerGetMetadata(t *testing.T) { "Duplicate data source type: test_foo", }, }, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov5.FunctionMetadata{}, + expectedResources: []tfprotov5.ResourceMetadata{}, + expectedServerCapabilities: &tfprotov5.ServerCapabilities{ + GetProviderSchemaOptional: true, + MoveResourceState: true, + PlanDestroy: true, + }, + }, + "duplicate-ephemeral-resource-type": { + servers: []func() tfprotov5.ProviderServer{ + (&tf5testserver.TestServer{ + GetMetadataResponse: &tfprotov5.GetMetadataResponse{ + EphemeralResources: []tfprotov5.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + }, + }, + }).ProviderServer, + (&tf5testserver.TestServer{ + GetMetadataResponse: &tfprotov5.GetMetadataResponse{ + EphemeralResources: []tfprotov5.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + }, + }, + }).ProviderServer, + }, + expectedDataSources: []tfprotov5.DataSourceMetadata{}, + expectedDiagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Invalid Provider Server Combination", + Detail: "The combined provider has multiple implementations of the same ephemeral resource type across underlying providers. " + + "Ephemeral resource types must be implemented by only one underlying provider. " + + "This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" + + "Duplicate ephemeral resource type: test_foo", + }, + }, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + }, expectedFunctions: []tfprotov5.FunctionMetadata{}, expectedResources: []tfprotov5.ResourceMetadata{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ @@ -190,6 +261,7 @@ func TestMuxServerGetMetadata(t *testing.T) { "Duplicate function: test_function", }, }, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, expectedFunctions: []tfprotov5.FunctionMetadata{ { Name: "test_function", @@ -234,7 +306,8 @@ func TestMuxServerGetMetadata(t *testing.T) { "Duplicate resource type: test_foo", }, }, - expectedFunctions: []tfprotov5.FunctionMetadata{}, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov5.FunctionMetadata{}, expectedResources: []tfprotov5.ResourceMetadata{ { TypeName: "test_foo", @@ -271,8 +344,9 @@ func TestMuxServerGetMetadata(t *testing.T) { }, }).ProviderServer, }, - expectedDataSources: []tfprotov5.DataSourceMetadata{}, - expectedFunctions: []tfprotov5.FunctionMetadata{}, + expectedDataSources: []tfprotov5.DataSourceMetadata{}, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov5.FunctionMetadata{}, expectedResources: []tfprotov5.ResourceMetadata{ { TypeName: "test_with_server_capabilities", @@ -311,8 +385,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: []tfprotov5.FunctionMetadata{}, - expectedResources: []tfprotov5.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov5.FunctionMetadata{}, + expectedResources: []tfprotov5.ResourceMetadata{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -358,8 +433,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: []tfprotov5.FunctionMetadata{}, - expectedResources: []tfprotov5.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov5.FunctionMetadata{}, + expectedResources: []tfprotov5.ResourceMetadata{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -390,8 +466,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test warning details", }, }, - expectedFunctions: []tfprotov5.FunctionMetadata{}, - expectedResources: []tfprotov5.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov5.FunctionMetadata{}, + expectedResources: []tfprotov5.ResourceMetadata{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -437,8 +514,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test warning details", }, }, - expectedFunctions: []tfprotov5.FunctionMetadata{}, - expectedResources: []tfprotov5.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov5.FunctionMetadata{}, + expectedResources: []tfprotov5.ResourceMetadata{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -484,8 +562,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: []tfprotov5.FunctionMetadata{}, - expectedResources: []tfprotov5.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov5.FunctionMetadata{}, + expectedResources: []tfprotov5.ResourceMetadata{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -520,6 +599,10 @@ func TestMuxServerGetMetadata(t *testing.T) { t.Errorf("diagnostics didn't match expectations: %s", diff) } + if diff := cmp.Diff(resp.EphemeralResources, testCase.expectedEphemeralResources); diff != "" { + t.Errorf("ephemeral resources didn't match expectations: %s", diff) + } + if diff := cmp.Diff(resp.Functions, testCase.expectedFunctions); diff != "" { t.Errorf("functions didn't match expectations: %s", diff) } diff --git a/tf5muxserver/mux_server_GetProviderSchema.go b/tf5muxserver/mux_server_GetProviderSchema.go index 19bfd87..70b2440 100644 --- a/tf5muxserver/mux_server_GetProviderSchema.go +++ b/tf5muxserver/mux_server_GetProviderSchema.go @@ -14,8 +14,8 @@ import ( // GetProviderSchema merges the schemas returned by the // tfprotov5.ProviderServers associated with muxServer into a single schema. -// Resources and data sources must be returned from only one server. Provider -// and ProviderMeta schemas must be identical between all servers. +// Resources, data sources, ephemeral resources, and functions must be returned +// from only one server. Provider and ProviderMeta schemas must be identical between all servers. func (s *muxServer) GetProviderSchema(ctx context.Context, req *tfprotov5.GetProviderSchemaRequest) (*tfprotov5.GetProviderSchemaResponse, error) { rpc := "GetProviderSchema" ctx = logging.InitContext(ctx) @@ -25,10 +25,11 @@ func (s *muxServer) GetProviderSchema(ctx context.Context, req *tfprotov5.GetPro defer s.serverDiscoveryMutex.Unlock() resp := &tfprotov5.GetProviderSchemaResponse{ - DataSourceSchemas: make(map[string]*tfprotov5.Schema), - Functions: make(map[string]*tfprotov5.Function), - ResourceSchemas: make(map[string]*tfprotov5.Schema), - ServerCapabilities: serverCapabilities, + DataSourceSchemas: make(map[string]*tfprotov5.Schema), + EphemeralResourceSchemas: make(map[string]*tfprotov5.Schema), + Functions: make(map[string]*tfprotov5.Function), + ResourceSchemas: make(map[string]*tfprotov5.Schema), + ServerCapabilities: serverCapabilities, } for _, server := range s.servers { @@ -106,6 +107,17 @@ func (s *muxServer) GetProviderSchema(ctx context.Context, req *tfprotov5.GetPro s.functions[name] = server resp.Functions[name] = definition } + + for ephemeralResourceType, schema := range serverResp.EphemeralResourceSchemas { + if _, ok := resp.EphemeralResourceSchemas[ephemeralResourceType]; ok { + resp.Diagnostics = append(resp.Diagnostics, ephemeralResourceDuplicateError(ephemeralResourceType)) + + continue + } + + s.ephemeralResources[ephemeralResourceType] = server + resp.EphemeralResourceSchemas[ephemeralResourceType] = schema + } } s.serverDiscoveryComplete = true diff --git a/tf5muxserver/mux_server_GetProviderSchema_test.go b/tf5muxserver/mux_server_GetProviderSchema_test.go index 3b7f72d..dc7bb6d 100644 --- a/tf5muxserver/mux_server_GetProviderSchema_test.go +++ b/tf5muxserver/mux_server_GetProviderSchema_test.go @@ -19,14 +19,15 @@ func TestMuxServerGetProviderSchema(t *testing.T) { t.Parallel() testCases := map[string]struct { - servers []func() tfprotov5.ProviderServer - expectedDataSourceSchemas map[string]*tfprotov5.Schema - expectedDiagnostics []*tfprotov5.Diagnostic - expectedFunctions map[string]*tfprotov5.Function - expectedProviderSchema *tfprotov5.Schema - expectedProviderMetaSchema *tfprotov5.Schema - expectedResourceSchemas map[string]*tfprotov5.Schema - expectedServerCapabilities *tfprotov5.ServerCapabilities + servers []func() tfprotov5.ProviderServer + expectedDataSourceSchemas map[string]*tfprotov5.Schema + expectedDiagnostics []*tfprotov5.Diagnostic + expectedEphemeralResourcesSchemas map[string]*tfprotov5.Schema + expectedFunctions map[string]*tfprotov5.Function + expectedProviderSchema *tfprotov5.Schema + expectedProviderMetaSchema *tfprotov5.Schema + expectedResourceSchemas map[string]*tfprotov5.Schema + expectedServerCapabilities *tfprotov5.ServerCapabilities }{ "combined": { servers: []func() tfprotov5.ProviderServer{ @@ -152,6 +153,45 @@ func TestMuxServerGetProviderSchema(t *testing.T) { }, }, }, + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_foo": { + Version: 1, + Block: &tfprotov5.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "secret_number", + Type: tftypes.Number, + Required: true, + Description: "input the secret number", + DescriptionKind: tfprotov5.StringKindPlain, + }, + }, + }, + }, + "test_ephemeral_bar": { + Version: 1, + Block: &tfprotov5.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "username", + Type: tftypes.String, + Optional: true, + Description: "your username", + DescriptionKind: tfprotov5.StringKindPlain, + }, + { + Name: "password", + Type: tftypes.String, + Optional: true, + Description: "your password", + DescriptionKind: tfprotov5.StringKindPlain, + }, + }, + }, + }, + }, }, }).ProviderServer, (&tf5testserver.TestServer{ @@ -279,6 +319,23 @@ func TestMuxServerGetProviderSchema(t *testing.T) { }, }, }, + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_foobar": { + Version: 1, + Block: &tfprotov5.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "secret_number", + Type: tftypes.Number, + Computed: true, + Description: "A generated secret number", + DescriptionKind: tfprotov5.StringKindPlain, + }, + }, + }, + }, + }, }, }).ProviderServer, }, @@ -462,6 +519,60 @@ func TestMuxServerGetProviderSchema(t *testing.T) { }, }, }, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_foo": { + Version: 1, + Block: &tfprotov5.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "secret_number", + Type: tftypes.Number, + Required: true, + Description: "input the secret number", + DescriptionKind: tfprotov5.StringKindPlain, + }, + }, + }, + }, + "test_ephemeral_bar": { + Version: 1, + Block: &tfprotov5.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "username", + Type: tftypes.String, + Optional: true, + Description: "your username", + DescriptionKind: tfprotov5.StringKindPlain, + }, + { + Name: "password", + Type: tftypes.String, + Optional: true, + Description: "your password", + DescriptionKind: tfprotov5.StringKindPlain, + }, + }, + }, + }, + "test_ephemeral_foobar": { + Version: 1, + Block: &tfprotov5.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "secret_number", + Type: tftypes.Number, + Computed: true, + Description: "A generated secret number", + DescriptionKind: tfprotov5.StringKindPlain, + }, + }, + }, + }, + }, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -498,6 +609,46 @@ func TestMuxServerGetProviderSchema(t *testing.T) { "Duplicate data source type: test_foo", }, }, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, + expectedResourceSchemas: map[string]*tfprotov5.Schema{}, + expectedServerCapabilities: &tfprotov5.ServerCapabilities{ + GetProviderSchemaOptional: true, + MoveResourceState: true, + PlanDestroy: true, + }, + }, + "duplicate-ephemeral-resource-type": { + servers: []func() tfprotov5.ProviderServer{ + (&tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_foo": {}, + }, + }, + }).ProviderServer, + (&tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_foo": {}, + }, + }, + }).ProviderServer, + }, + expectedDataSourceSchemas: map[string]*tfprotov5.Schema{}, + expectedDiagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Invalid Provider Server Combination", + Detail: "The combined provider has multiple implementations of the same ephemeral resource type across underlying providers. " + + "Ephemeral resource types must be implemented by only one underlying provider. " + + "This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" + + "Duplicate ephemeral resource type: test_foo", + }, + }, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{ + "test_foo": {}, + }, expectedFunctions: map[string]*tfprotov5.Function{}, expectedResourceSchemas: map[string]*tfprotov5.Schema{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ @@ -534,6 +685,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { "Duplicate function: test_function", }, }, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, expectedFunctions: map[string]*tfprotov5.Function{ "test_function": {}, }, @@ -572,7 +724,8 @@ func TestMuxServerGetProviderSchema(t *testing.T) { "Duplicate resource type: test_foo", }, }, - expectedFunctions: map[string]*tfprotov5.Function{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, expectedResourceSchemas: map[string]*tfprotov5.Schema{ "test_foo": {}, }, @@ -649,7 +802,8 @@ func TestMuxServerGetProviderSchema(t *testing.T) { ), }, }, - expectedFunctions: map[string]*tfprotov5.Function{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, expectedProviderSchema: &tfprotov5.Schema{ Block: &tfprotov5.SchemaBlock{ Attributes: []*tfprotov5.SchemaAttribute{ @@ -735,7 +889,8 @@ func TestMuxServerGetProviderSchema(t *testing.T) { ), }, }, - expectedFunctions: map[string]*tfprotov5.Function{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, expectedProviderMetaSchema: &tfprotov5.Schema{ Block: &tfprotov5.SchemaBlock{ Attributes: []*tfprotov5.SchemaAttribute{ @@ -775,8 +930,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { }, }).ProviderServer, }, - expectedDataSourceSchemas: map[string]*tfprotov5.Schema{}, - expectedFunctions: map[string]*tfprotov5.Function{}, + expectedDataSourceSchemas: map[string]*tfprotov5.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, expectedResourceSchemas: map[string]*tfprotov5.Schema{ "test_with_server_capabilities": {}, "test_without_server_capabilities": {}, @@ -811,8 +967,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: map[string]*tfprotov5.Function{}, - expectedResourceSchemas: map[string]*tfprotov5.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, + expectedResourceSchemas: map[string]*tfprotov5.Schema{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -858,8 +1015,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: map[string]*tfprotov5.Function{}, - expectedResourceSchemas: map[string]*tfprotov5.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, + expectedResourceSchemas: map[string]*tfprotov5.Schema{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -890,8 +1048,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test warning details", }, }, - expectedFunctions: map[string]*tfprotov5.Function{}, - expectedResourceSchemas: map[string]*tfprotov5.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, + expectedResourceSchemas: map[string]*tfprotov5.Schema{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -937,8 +1096,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test warning details", }, }, - expectedFunctions: map[string]*tfprotov5.Function{}, - expectedResourceSchemas: map[string]*tfprotov5.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, + expectedResourceSchemas: map[string]*tfprotov5.Schema{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -984,8 +1144,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: map[string]*tfprotov5.Function{}, - expectedResourceSchemas: map[string]*tfprotov5.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov5.Schema{}, + expectedFunctions: map[string]*tfprotov5.Function{}, + expectedResourceSchemas: map[string]*tfprotov5.Schema{}, expectedServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -1020,6 +1181,10 @@ func TestMuxServerGetProviderSchema(t *testing.T) { t.Errorf("diagnostics didn't match expectations: %s", diff) } + if diff := cmp.Diff(resp.EphemeralResourceSchemas, testCase.expectedEphemeralResourcesSchemas); diff != "" { + t.Errorf("ephemeral resources schemas didn't match expectations: %s", diff) + } + if diff := cmp.Diff(resp.Functions, testCase.expectedFunctions); diff != "" { t.Errorf("functions didn't match expectations: %s", diff) } diff --git a/tf5muxserver/mux_server_OpenEphemeralResource.go b/tf5muxserver/mux_server_OpenEphemeralResource.go new file mode 100644 index 0000000..e40a2b7 --- /dev/null +++ b/tf5muxserver/mux_server_OpenEphemeralResource.go @@ -0,0 +1,35 @@ +// 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) OpenEphemeralResource(ctx context.Context, req *tfprotov5.OpenEphemeralResourceRequest) (*tfprotov5.OpenEphemeralResourceResponse, error) { + rpc := "OpenEphemeralResource" + ctx = logging.InitContext(ctx) + ctx = logging.RpcContext(ctx, rpc) + + server, diags, err := s.getEphemeralResourceServer(ctx, req.TypeName) + + if err != nil { + return nil, err + } + + if diagnosticsHasError(diags) { + return &tfprotov5.OpenEphemeralResourceResponse{ + Diagnostics: diags, + }, nil + } + + ctx = logging.Tfprotov5ProviderServerContext(ctx, server) + logging.MuxTrace(ctx, "calling downstream server") + + return server.OpenEphemeralResource(ctx, req) +} diff --git a/tf5muxserver/mux_server_OpenEphemeralResource_test.go b/tf5muxserver/mux_server_OpenEphemeralResource_test.go new file mode 100644 index 0000000..970fa99 --- /dev/null +++ b/tf5muxserver/mux_server_OpenEphemeralResource_test.go @@ -0,0 +1,72 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf5muxserver_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + + "github.com/hashicorp/terraform-plugin-mux/internal/tf5testserver" + "github.com/hashicorp/terraform-plugin-mux/tf5muxserver" +) + +func TestMuxServerOpenEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testServer1 := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource_server1": {}, + }, + }, + } + testServer2 := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource_server2": {}, + }, + }, + } + servers := []func() tfprotov5.ProviderServer{testServer1.ProviderServer, testServer2.ProviderServer} + muxServer, err := tf5muxserver.NewMuxServer(ctx, servers...) + + if err != nil { + t.Fatalf("unexpected error setting up factory: %s", err) + } + + _, err = muxServer.ProviderServer().OpenEphemeralResource(ctx, &tfprotov5.OpenEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server1", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !testServer1.OpenEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("expected test_ephemeral_resource_server1 OpenEphemeralResource to be called on server1") + } + + if testServer2.OpenEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("unexpected test_ephemeral_resource_server1 OpenEphemeralResource called on server2") + } + + _, err = muxServer.ProviderServer().OpenEphemeralResource(ctx, &tfprotov5.OpenEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server2", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if testServer1.OpenEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("unexpected test_ephemeral_resource_server2 OpenEphemeralResource called on server1") + } + + if !testServer2.OpenEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("expected test_ephemeral_resource_server2 OpenEphemeralResource to be called on server2") + } +} diff --git a/tf5muxserver/mux_server_RenewEphemeralResource.go b/tf5muxserver/mux_server_RenewEphemeralResource.go new file mode 100644 index 0000000..bc6bcc6 --- /dev/null +++ b/tf5muxserver/mux_server_RenewEphemeralResource.go @@ -0,0 +1,35 @@ +// 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) RenewEphemeralResource(ctx context.Context, req *tfprotov5.RenewEphemeralResourceRequest) (*tfprotov5.RenewEphemeralResourceResponse, error) { + rpc := "RenewEphemeralResource" + ctx = logging.InitContext(ctx) + ctx = logging.RpcContext(ctx, rpc) + + server, diags, err := s.getEphemeralResourceServer(ctx, req.TypeName) + + if err != nil { + return nil, err + } + + if diagnosticsHasError(diags) { + return &tfprotov5.RenewEphemeralResourceResponse{ + Diagnostics: diags, + }, nil + } + + ctx = logging.Tfprotov5ProviderServerContext(ctx, server) + logging.MuxTrace(ctx, "calling downstream server") + + return server.RenewEphemeralResource(ctx, req) +} diff --git a/tf5muxserver/mux_server_RenewEphemeralResource_test.go b/tf5muxserver/mux_server_RenewEphemeralResource_test.go new file mode 100644 index 0000000..efd7b0b --- /dev/null +++ b/tf5muxserver/mux_server_RenewEphemeralResource_test.go @@ -0,0 +1,72 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf5muxserver_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + + "github.com/hashicorp/terraform-plugin-mux/internal/tf5testserver" + "github.com/hashicorp/terraform-plugin-mux/tf5muxserver" +) + +func TestMuxServerRenewEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testServer1 := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource_server1": {}, + }, + }, + } + testServer2 := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource_server2": {}, + }, + }, + } + servers := []func() tfprotov5.ProviderServer{testServer1.ProviderServer, testServer2.ProviderServer} + muxServer, err := tf5muxserver.NewMuxServer(ctx, servers...) + + if err != nil { + t.Fatalf("unexpected error setting up factory: %s", err) + } + + _, err = muxServer.ProviderServer().RenewEphemeralResource(ctx, &tfprotov5.RenewEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server1", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !testServer1.RenewEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("expected test_ephemeral_resource_server1 RenewEphemeralResource to be called on server1") + } + + if testServer2.RenewEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("unexpected test_ephemeral_resource_server1 RenewEphemeralResource called on server2") + } + + _, err = muxServer.ProviderServer().RenewEphemeralResource(ctx, &tfprotov5.RenewEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server2", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if testServer1.RenewEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("unexpected test_ephemeral_resource_server2 RenewEphemeralResource called on server1") + } + + if !testServer2.RenewEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("expected test_ephemeral_resource_server2 RenewEphemeralResource to be called on server2") + } +} diff --git a/tf5muxserver/mux_server_ValidateEphemeralResourceConfig.go b/tf5muxserver/mux_server_ValidateEphemeralResourceConfig.go new file mode 100644 index 0000000..0f7ccf3 --- /dev/null +++ b/tf5muxserver/mux_server_ValidateEphemeralResourceConfig.go @@ -0,0 +1,35 @@ +// 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) ValidateEphemeralResourceConfig(ctx context.Context, req *tfprotov5.ValidateEphemeralResourceConfigRequest) (*tfprotov5.ValidateEphemeralResourceConfigResponse, error) { + rpc := "ValidateEphemeralResourceTypeConfig" + ctx = logging.InitContext(ctx) + ctx = logging.RpcContext(ctx, rpc) + + server, diags, err := s.getEphemeralResourceServer(ctx, req.TypeName) + + if err != nil { + return nil, err + } + + if diagnosticsHasError(diags) { + return &tfprotov5.ValidateEphemeralResourceConfigResponse{ + Diagnostics: diags, + }, nil + } + + ctx = logging.Tfprotov5ProviderServerContext(ctx, server) + logging.MuxTrace(ctx, "calling downstream server") + + return server.ValidateEphemeralResourceConfig(ctx, req) +} diff --git a/tf5muxserver/mux_server_ValidateEphemeralResourceConfig_test.go b/tf5muxserver/mux_server_ValidateEphemeralResourceConfig_test.go new file mode 100644 index 0000000..21f0df7 --- /dev/null +++ b/tf5muxserver/mux_server_ValidateEphemeralResourceConfig_test.go @@ -0,0 +1,72 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf5muxserver_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + + "github.com/hashicorp/terraform-plugin-mux/internal/tf5testserver" + "github.com/hashicorp/terraform-plugin-mux/tf5muxserver" +) + +func TestMuxServerValidateEphemeralResourceConfig(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testServer1 := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource_server1": {}, + }, + }, + } + testServer2 := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource_server2": {}, + }, + }, + } + servers := []func() tfprotov5.ProviderServer{testServer1.ProviderServer, testServer2.ProviderServer} + muxServer, err := tf5muxserver.NewMuxServer(ctx, servers...) + + if err != nil { + t.Fatalf("unexpected error setting up factory: %s", err) + } + + _, err = muxServer.ProviderServer().ValidateEphemeralResourceConfig(ctx, &tfprotov5.ValidateEphemeralResourceConfigRequest{ + TypeName: "test_ephemeral_resource_server1", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !testServer1.ValidateEphemeralResourceConfigCalled["test_ephemeral_resource_server1"] { + t.Errorf("expected test_ephemeral_resource_server1 ValidateEphemeralResourceConfig to be called on server1") + } + + if testServer2.ValidateEphemeralResourceConfigCalled["test_ephemeral_resource_server1"] { + t.Errorf("unexpected test_ephemeral_resource_server1 ValidateEphemeralResourceConfig called on server2") + } + + _, err = muxServer.ProviderServer().ValidateEphemeralResourceConfig(ctx, &tfprotov5.ValidateEphemeralResourceConfigRequest{ + TypeName: "test_ephemeral_resource_server2", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if testServer1.ValidateEphemeralResourceConfigCalled["test_ephemeral_resource_server2"] { + t.Errorf("unexpected test_ephemeral_resource_server2 ValidateEphemeralResourceConfig called on server1") + } + + if !testServer2.ValidateEphemeralResourceConfigCalled["test_ephemeral_resource_server2"] { + t.Errorf("expected test_ephemeral_resource_server2 ValidateEphemeralResourceConfig to be called on server2") + } +} diff --git a/tf5to6server/tf5to6server.go b/tf5to6server/tf5to6server.go index 2a81363..27f1538 100644 --- a/tf5to6server/tf5to6server.go +++ b/tf5to6server/tf5to6server.go @@ -55,6 +55,17 @@ func (s v5tov6Server) CallFunction(ctx context.Context, req *tfprotov6.CallFunct return tfprotov5tov6.CallFunctionResponse(v5Resp), nil } +func (s v5tov6Server) CloseEphemeralResource(ctx context.Context, req *tfprotov6.CloseEphemeralResourceRequest) (*tfprotov6.CloseEphemeralResourceResponse, error) { + v5Req := tfprotov6tov5.CloseEphemeralResourceRequest(req) + + v5Resp, err := s.v5Server.CloseEphemeralResource(ctx, v5Req) + if err != nil { + return nil, err + } + + return tfprotov5tov6.CloseEphemeralResourceResponse(v5Resp), nil +} + func (s v5tov6Server) ConfigureProvider(ctx context.Context, req *tfprotov6.ConfigureProviderRequest) (*tfprotov6.ConfigureProviderResponse, error) { v5Req := tfprotov6tov5.ConfigureProviderRequest(req) v5Resp, err := s.v5Server.ConfigureProvider(ctx, v5Req) @@ -121,6 +132,17 @@ func (s v5tov6Server) MoveResourceState(ctx context.Context, req *tfprotov6.Move return tfprotov5tov6.MoveResourceStateResponse(v5Resp), nil } +func (s v5tov6Server) OpenEphemeralResource(ctx context.Context, req *tfprotov6.OpenEphemeralResourceRequest) (*tfprotov6.OpenEphemeralResourceResponse, error) { + v5Req := tfprotov6tov5.OpenEphemeralResourceRequest(req) + + v5Resp, err := s.v5Server.OpenEphemeralResource(ctx, v5Req) + if err != nil { + return nil, err + } + + return tfprotov5tov6.OpenEphemeralResourceResponse(v5Resp), nil +} + func (s v5tov6Server) PlanResourceChange(ctx context.Context, req *tfprotov6.PlanResourceChangeRequest) (*tfprotov6.PlanResourceChangeResponse, error) { v5Req := tfprotov6tov5.PlanResourceChangeRequest(req) v5Resp, err := s.v5Server.PlanResourceChange(ctx, v5Req) @@ -159,6 +181,17 @@ func (s v5tov6Server) ReadResource(ctx context.Context, req *tfprotov6.ReadResou return tfprotov5tov6.ReadResourceResponse(v5Resp), nil } +func (s v5tov6Server) RenewEphemeralResource(ctx context.Context, req *tfprotov6.RenewEphemeralResourceRequest) (*tfprotov6.RenewEphemeralResourceResponse, error) { + v5Req := tfprotov6tov5.RenewEphemeralResourceRequest(req) + + v5Resp, err := s.v5Server.RenewEphemeralResource(ctx, v5Req) + if err != nil { + return nil, err + } + + return tfprotov5tov6.RenewEphemeralResourceResponse(v5Resp), nil +} + func (s v5tov6Server) StopProvider(ctx context.Context, req *tfprotov6.StopProviderRequest) (*tfprotov6.StopProviderResponse, error) { v5Req := tfprotov6tov5.StopProviderRequest(req) v5Resp, err := s.v5Server.StopProvider(ctx, v5Req) @@ -192,6 +225,17 @@ func (s v5tov6Server) ValidateDataResourceConfig(ctx context.Context, req *tfpro return tfprotov5tov6.ValidateDataResourceConfigResponse(v5Resp), nil } +func (s v5tov6Server) ValidateEphemeralResourceConfig(ctx context.Context, req *tfprotov6.ValidateEphemeralResourceConfigRequest) (*tfprotov6.ValidateEphemeralResourceConfigResponse, error) { + v5Req := tfprotov6tov5.ValidateEphemeralResourceConfigRequest(req) + v5Resp, err := s.v5Server.ValidateEphemeralResourceConfig(ctx, v5Req) + + if err != nil { + return nil, err + } + + return tfprotov5tov6.ValidateEphemeralResourceConfigResponse(v5Resp), nil +} + func (s v5tov6Server) ValidateProviderConfig(ctx context.Context, req *tfprotov6.ValidateProviderConfigRequest) (*tfprotov6.ValidateProviderConfigResponse, error) { v5Req := tfprotov6tov5.PrepareProviderConfigRequest(req) v5Resp, err := s.v5Server.PrepareProviderConfig(ctx, v5Req) diff --git a/tf5to6server/tf5to6server_test.go b/tf5to6server/tf5to6server_test.go index 3296e18..d0f32ec 100644 --- a/tf5to6server/tf5to6server_test.go +++ b/tf5to6server/tf5to6server_test.go @@ -29,6 +29,9 @@ func TestUpgradeServer(t *testing.T) { DataSourceSchemas: map[string]*tfprotov5.Schema{ "test_data_source": {}, }, + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource": {}, + }, Functions: map[string]*tfprotov5.Function{ "test_function": {}, }, @@ -191,6 +194,37 @@ func TestV6ToV5ServerCallFunction(t *testing.T) { } } +func TestV6ToV5ServerCloseEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + v5server := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource": {}, + }, + }, + } + + v6server, err := tf5to6server.UpgradeServer(context.Background(), v5server.ProviderServer) + + if err != nil { + t.Fatalf("unexpected error downgrading server: %s", err) + } + + _, err = v6server.CloseEphemeralResource(ctx, &tfprotov6.CloseEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !v5server.CloseEphemeralResourceCalled["test_ephemeral_resource"] { + t.Errorf("expected test_ephemeral_resource CloseEphemeralResource to be called") + } +} + func TestV6ToV5ServerConfigureProvider(t *testing.T) { t.Parallel() @@ -371,6 +405,37 @@ func TestV5ToV6ServerMoveResourceState(t *testing.T) { } } +func TestV6ToV5ServerOpenEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + v5server := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource": {}, + }, + }, + } + + v6server, err := tf5to6server.UpgradeServer(context.Background(), v5server.ProviderServer) + + if err != nil { + t.Fatalf("unexpected error downgrading server: %s", err) + } + + _, err = v6server.OpenEphemeralResource(ctx, &tfprotov6.OpenEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !v5server.OpenEphemeralResourceCalled["test_ephemeral_resource"] { + t.Errorf("expected test_ephemeral_resource OpenEphemeralResource to be called") + } +} + func TestV6ToV5ServerPlanResourceChange(t *testing.T) { t.Parallel() @@ -464,6 +529,37 @@ func TestV6ToV5ServerReadResource(t *testing.T) { } } +func TestV6ToV5ServerRenewEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + v5server := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource": {}, + }, + }, + } + + v6server, err := tf5to6server.UpgradeServer(context.Background(), v5server.ProviderServer) + + if err != nil { + t.Fatalf("unexpected error downgrading server: %s", err) + } + + _, err = v6server.RenewEphemeralResource(ctx, &tfprotov6.RenewEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !v5server.RenewEphemeralResourceCalled["test_ephemeral_resource"] { + t.Errorf("expected test_ephemeral_resource RenewEphemeralResource to be called") + } +} + func TestV6ToV5ServerStopProvider(t *testing.T) { t.Parallel() @@ -555,6 +651,37 @@ func TestV6ToV5ServerValidateDataResourceConfig(t *testing.T) { } } +func TestV6ToV5ServerValidateEphemeralResourceConfig(t *testing.T) { + t.Parallel() + + ctx := context.Background() + v5server := &tf5testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov5.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_resource": {}, + }, + }, + } + + v6server, err := tf5to6server.UpgradeServer(context.Background(), v5server.ProviderServer) + + if err != nil { + t.Fatalf("unexpected error downgrading server: %s", err) + } + + _, err = v6server.ValidateEphemeralResourceConfig(ctx, &tfprotov6.ValidateEphemeralResourceConfigRequest{ + TypeName: "test_resource", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !v5server.ValidateEphemeralResourceConfigCalled["test_resource"] { + t.Errorf("expected test_resource ValidateEphemeralResourceConfig to be called") + } +} + func TestV6ToV5ServerValidateProviderConfig(t *testing.T) { t.Parallel() diff --git a/tf6muxserver/diagnostics.go b/tf6muxserver/diagnostics.go index 80572fc..9e4bb29 100644 --- a/tf6muxserver/diagnostics.go +++ b/tf6muxserver/diagnostics.go @@ -28,6 +28,27 @@ func dataSourceMissingError(typeName string) *tfprotov6.Diagnostic { } } +func ephemeralResourceDuplicateError(typeName string) *tfprotov6.Diagnostic { + return &tfprotov6.Diagnostic{ + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Invalid Provider Server Combination", + Detail: "The combined provider has multiple implementations of the same ephemeral resource type across underlying providers. " + + "Ephemeral resource types must be implemented by only one underlying provider. " + + "This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" + + "Duplicate ephemeral resource type: " + typeName, + } +} + +func ephemeralResourceMissingError(typeName string) *tfprotov6.Diagnostic { + return &tfprotov6.Diagnostic{ + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Ephemeral Resource Not Implemented", + Detail: "The combined provider does not implement the requested ephemeral resource type. " + + "This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" + + "Missing ephemeral resource type: " + typeName, + } +} + func diagnosticsHasError(diagnostics []*tfprotov6.Diagnostic) bool { for _, diagnostic := range diagnostics { if diagnostic == nil { diff --git a/tf6muxserver/mux_server.go b/tf6muxserver/mux_server.go index 243f4c7..b83bb56 100644 --- a/tf6muxserver/mux_server.go +++ b/tf6muxserver/mux_server.go @@ -8,9 +8,10 @@ import ( "sync" "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-mux/internal/logging" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/hashicorp/terraform-plugin-mux/internal/logging" ) var _ tfprotov6.ProviderServer = &muxServer{} @@ -22,6 +23,9 @@ type muxServer struct { // Routing for data source types dataSources map[string]tfprotov6.ProviderServer + // Routing for ephemeral resource types + ephemeralResources map[string]tfprotov6.ProviderServer + // Routing for functions functions map[string]tfprotov6.ProviderServer @@ -90,6 +94,41 @@ func (s *muxServer) getDataSourceServer(ctx context.Context, typeName string) (t return server, s.serverDiscoveryDiagnostics, nil } +func (s *muxServer) getEphemeralResourceServer(ctx context.Context, typeName string) (tfprotov6.ProviderServer, []*tfprotov6.Diagnostic, error) { + s.serverDiscoveryMutex.RLock() + server, ok := s.ephemeralResources[typeName] + discoveryComplete := s.serverDiscoveryComplete + s.serverDiscoveryMutex.RUnlock() + + if discoveryComplete { + if ok { + return server, s.serverDiscoveryDiagnostics, nil + } + + return nil, []*tfprotov6.Diagnostic{ + ephemeralResourceMissingError(typeName), + }, nil + } + + err := s.serverDiscovery(ctx) + + if err != nil || diagnosticsHasError(s.serverDiscoveryDiagnostics) { + return nil, s.serverDiscoveryDiagnostics, err + } + + s.serverDiscoveryMutex.RLock() + server, ok = s.ephemeralResources[typeName] + s.serverDiscoveryMutex.RUnlock() + + if !ok { + return nil, []*tfprotov6.Diagnostic{ + ephemeralResourceMissingError(typeName), + }, nil + } + + return server, s.serverDiscoveryDiagnostics, nil +} + func (s *muxServer) getFunctionServer(ctx context.Context, name string) (tfprotov6.ProviderServer, []*tfprotov6.Diagnostic, error) { s.serverDiscoveryMutex.RLock() server, ok := s.functions[name] @@ -163,7 +202,7 @@ func (s *muxServer) getResourceServer(ctx context.Context, typeName string) (tfp // serverDiscovery will populate the mux server "routing" for functions and // resource types by calling all underlying server GetMetadata RPC and falling // back to GetProviderSchema RPC. It is intended to only be called through -// getDataSourceServer, getFunctionServer, and getResourceServer. +// getDataSourceServer, getEphemeralResourceServer, getFunctionServer, and getResourceServer. // // The error return represents gRPC errors, which except for the GetMetadata // call returning the gRPC unimplemented error, is always returned. @@ -201,6 +240,16 @@ func (s *muxServer) serverDiscovery(ctx context.Context) error { s.dataSources[serverDataSource.TypeName] = server } + for _, serverEphemeralResource := range metadataResp.EphemeralResources { + if _, ok := s.ephemeralResources[serverEphemeralResource.TypeName]; ok { + s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, ephemeralResourceDuplicateError(serverEphemeralResource.TypeName)) + + continue + } + + s.ephemeralResources[serverEphemeralResource.TypeName] = server + } + for _, serverFunction := range metadataResp.Functions { if _, ok := s.functions[serverFunction.Name]; ok { s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, functionDuplicateError(serverFunction.Name)) @@ -253,6 +302,16 @@ func (s *muxServer) serverDiscovery(ctx context.Context) error { s.dataSources[typeName] = server } + for typeName := range providerSchemaResp.EphemeralResourceSchemas { + if _, ok := s.ephemeralResources[typeName]; ok { + s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, ephemeralResourceDuplicateError(typeName)) + + continue + } + + s.ephemeralResources[typeName] = server + } + for name := range providerSchemaResp.Functions { if _, ok := s.functions[name]; ok { s.serverDiscoveryDiagnostics = append(s.serverDiscoveryDiagnostics, functionDuplicateError(name)) @@ -290,9 +349,11 @@ func (s *muxServer) serverDiscovery(ctx context.Context) error { // - Only one provider implements each managed resource // - Only one provider implements each data source // - Only one provider implements each function +// - Only one provider implements each ephemeral resource func NewMuxServer(_ context.Context, servers ...func() tfprotov6.ProviderServer) (*muxServer, error) { result := muxServer{ dataSources: make(map[string]tfprotov6.ProviderServer), + ephemeralResources: make(map[string]tfprotov6.ProviderServer), functions: make(map[string]tfprotov6.ProviderServer), resources: make(map[string]tfprotov6.ProviderServer), resourceCapabilities: make(map[string]*tfprotov6.ServerCapabilities), diff --git a/tf6muxserver/mux_server_CloseEphemeralResource.go b/tf6muxserver/mux_server_CloseEphemeralResource.go new file mode 100644 index 0000000..35147da --- /dev/null +++ b/tf6muxserver/mux_server_CloseEphemeralResource.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf6muxserver + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-mux/internal/logging" +) + +func (s *muxServer) CloseEphemeralResource(ctx context.Context, req *tfprotov6.CloseEphemeralResourceRequest) (*tfprotov6.CloseEphemeralResourceResponse, error) { + rpc := "CloseEphemeralResource" + ctx = logging.InitContext(ctx) + ctx = logging.RpcContext(ctx, rpc) + + server, diags, err := s.getEphemeralResourceServer(ctx, req.TypeName) + + if err != nil { + return nil, err + } + + if diagnosticsHasError(diags) { + return &tfprotov6.CloseEphemeralResourceResponse{ + Diagnostics: diags, + }, nil + } + + ctx = logging.Tfprotov6ProviderServerContext(ctx, server) + logging.MuxTrace(ctx, "calling downstream server") + + return server.CloseEphemeralResource(ctx, req) +} diff --git a/tf6muxserver/mux_server_CloseEphemeralResource_test.go b/tf6muxserver/mux_server_CloseEphemeralResource_test.go new file mode 100644 index 0000000..c8f7cff --- /dev/null +++ b/tf6muxserver/mux_server_CloseEphemeralResource_test.go @@ -0,0 +1,72 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf6muxserver_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-mux/internal/tf6testserver" + "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" +) + +func TestMuxServerCloseEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testServer1 := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource_server1": {}, + }, + }, + } + testServer2 := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource_server2": {}, + }, + }, + } + servers := []func() tfprotov6.ProviderServer{testServer1.ProviderServer, testServer2.ProviderServer} + muxServer, err := tf6muxserver.NewMuxServer(ctx, servers...) + + if err != nil { + t.Fatalf("unexpected error setting up factory: %s", err) + } + + _, err = muxServer.ProviderServer().CloseEphemeralResource(ctx, &tfprotov6.CloseEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server1", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !testServer1.CloseEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("expected test_ephemeral_resource_server1 CloseEphemeralResource to be called on server1") + } + + if testServer2.CloseEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("unexpected test_ephemeral_resource_server1 CloseEphemeralResource called on server2") + } + + _, err = muxServer.ProviderServer().CloseEphemeralResource(ctx, &tfprotov6.CloseEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server2", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if testServer1.CloseEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("unexpected test_ephemeral_resource_server2 CloseEphemeralResource called on server1") + } + + if !testServer2.CloseEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("expected test_ephemeral_resource_server2 CloseEphemeralResource to be called on server2") + } +} diff --git a/tf6muxserver/mux_server_GetMetadata.go b/tf6muxserver/mux_server_GetMetadata.go index a181654..8a2f1e5 100644 --- a/tf6muxserver/mux_server_GetMetadata.go +++ b/tf6muxserver/mux_server_GetMetadata.go @@ -26,6 +26,7 @@ func (s *muxServer) GetMetadata(ctx context.Context, req *tfprotov6.GetMetadataR resp := &tfprotov6.GetMetadataResponse{ DataSources: make([]tfprotov6.DataSourceMetadata, 0), + EphemeralResources: make([]tfprotov6.EphemeralResourceMetadata, 0), Functions: make([]tfprotov6.FunctionMetadata, 0), Resources: make([]tfprotov6.ResourceMetadata, 0), ServerCapabilities: serverCapabilities, @@ -54,6 +55,17 @@ func (s *muxServer) GetMetadata(ctx context.Context, req *tfprotov6.GetMetadataR resp.DataSources = append(resp.DataSources, datasource) } + for _, ephemeralResource := range serverResp.EphemeralResources { + if ephemeralResourceMetadataContainsTypeName(resp.EphemeralResources, ephemeralResource.TypeName) { + resp.Diagnostics = append(resp.Diagnostics, ephemeralResourceDuplicateError(ephemeralResource.TypeName)) + + continue + } + + s.ephemeralResources[ephemeralResource.TypeName] = server + resp.EphemeralResources = append(resp.EphemeralResources, ephemeralResource) + } + for _, function := range serverResp.Functions { if functionMetadataContainsName(resp.Functions, function.Name) { resp.Diagnostics = append(resp.Diagnostics, functionDuplicateError(function.Name)) @@ -91,6 +103,16 @@ func datasourceMetadataContainsTypeName(metadatas []tfprotov6.DataSourceMetadata return false } +func ephemeralResourceMetadataContainsTypeName(metadatas []tfprotov6.EphemeralResourceMetadata, typeName string) bool { + for _, metadata := range metadatas { + if typeName == metadata.TypeName { + return true + } + } + + return false +} + func functionMetadataContainsName(metadatas []tfprotov6.FunctionMetadata, name string) bool { for _, metadata := range metadatas { if name == metadata.Name { diff --git a/tf6muxserver/mux_server_GetMetadata_test.go b/tf6muxserver/mux_server_GetMetadata_test.go index b417e81..c7b97df 100644 --- a/tf6muxserver/mux_server_GetMetadata_test.go +++ b/tf6muxserver/mux_server_GetMetadata_test.go @@ -21,6 +21,7 @@ func TestMuxServerGetMetadata(t *testing.T) { servers []func() tfprotov6.ProviderServer expectedDataSources []tfprotov6.DataSourceMetadata expectedDiagnostics []*tfprotov6.Diagnostic + expectedEphemeralResources []tfprotov6.EphemeralResourceMetadata expectedFunctions []tfprotov6.FunctionMetadata expectedResources []tfprotov6.ResourceMetadata expectedServerCapabilities *tfprotov6.ServerCapabilities @@ -47,6 +48,14 @@ func TestMuxServerGetMetadata(t *testing.T) { Name: "test_function1", }, }, + EphemeralResources: []tfprotov6.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + { + TypeName: "test_bar", + }, + }, }, }).ProviderServer, (&tf6testserver.TestServer{ @@ -72,6 +81,11 @@ func TestMuxServerGetMetadata(t *testing.T) { Name: "test_function3", }, }, + EphemeralResources: []tfprotov6.EphemeralResourceMetadata{ + { + TypeName: "test_quux", + }, + }, }, }).ProviderServer, }, @@ -108,6 +122,17 @@ func TestMuxServerGetMetadata(t *testing.T) { Name: "test_function3", }, }, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + { + TypeName: "test_bar", + }, + { + TypeName: "test_quux", + }, + }, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -150,6 +175,52 @@ func TestMuxServerGetMetadata(t *testing.T) { "Duplicate data source type: test_foo", }, }, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov6.FunctionMetadata{}, + expectedResources: []tfprotov6.ResourceMetadata{}, + expectedServerCapabilities: &tfprotov6.ServerCapabilities{ + GetProviderSchemaOptional: true, + MoveResourceState: true, + PlanDestroy: true, + }, + }, + "duplicate-ephemeral-resource-type": { + servers: []func() tfprotov6.ProviderServer{ + (&tf6testserver.TestServer{ + GetMetadataResponse: &tfprotov6.GetMetadataResponse{ + EphemeralResources: []tfprotov6.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + }, + }, + }).ProviderServer, + (&tf6testserver.TestServer{ + GetMetadataResponse: &tfprotov6.GetMetadataResponse{ + EphemeralResources: []tfprotov6.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + }, + }, + }).ProviderServer, + }, + expectedDataSources: []tfprotov6.DataSourceMetadata{}, + expectedDiagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Invalid Provider Server Combination", + Detail: "The combined provider has multiple implementations of the same ephemeral resource type across underlying providers. " + + "Ephemeral resource types must be implemented by only one underlying provider. " + + "This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" + + "Duplicate ephemeral resource type: test_foo", + }, + }, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{ + { + TypeName: "test_foo", + }, + }, expectedFunctions: []tfprotov6.FunctionMetadata{}, expectedResources: []tfprotov6.ResourceMetadata{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ @@ -190,6 +261,7 @@ func TestMuxServerGetMetadata(t *testing.T) { "Duplicate function: test_function", }, }, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{}, expectedFunctions: []tfprotov6.FunctionMetadata{ { Name: "test_function", @@ -234,7 +306,8 @@ func TestMuxServerGetMetadata(t *testing.T) { "Duplicate resource type: test_foo", }, }, - expectedFunctions: []tfprotov6.FunctionMetadata{}, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov6.FunctionMetadata{}, expectedResources: []tfprotov6.ResourceMetadata{ { TypeName: "test_foo", @@ -271,8 +344,9 @@ func TestMuxServerGetMetadata(t *testing.T) { }, }).ProviderServer, }, - expectedDataSources: []tfprotov6.DataSourceMetadata{}, - expectedFunctions: []tfprotov6.FunctionMetadata{}, + expectedDataSources: []tfprotov6.DataSourceMetadata{}, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov6.FunctionMetadata{}, expectedResources: []tfprotov6.ResourceMetadata{ { TypeName: "test_with_server_capabilities", @@ -311,8 +385,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: []tfprotov6.FunctionMetadata{}, - expectedResources: []tfprotov6.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov6.FunctionMetadata{}, + expectedResources: []tfprotov6.ResourceMetadata{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -358,8 +433,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: []tfprotov6.FunctionMetadata{}, - expectedResources: []tfprotov6.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov6.FunctionMetadata{}, + expectedResources: []tfprotov6.ResourceMetadata{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -390,8 +466,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test warning details", }, }, - expectedFunctions: []tfprotov6.FunctionMetadata{}, - expectedResources: []tfprotov6.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov6.FunctionMetadata{}, + expectedResources: []tfprotov6.ResourceMetadata{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -437,8 +514,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test warning details", }, }, - expectedFunctions: []tfprotov6.FunctionMetadata{}, - expectedResources: []tfprotov6.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov6.FunctionMetadata{}, + expectedResources: []tfprotov6.ResourceMetadata{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -484,8 +562,9 @@ func TestMuxServerGetMetadata(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: []tfprotov6.FunctionMetadata{}, - expectedResources: []tfprotov6.ResourceMetadata{}, + expectedEphemeralResources: []tfprotov6.EphemeralResourceMetadata{}, + expectedFunctions: []tfprotov6.FunctionMetadata{}, + expectedResources: []tfprotov6.ResourceMetadata{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -524,6 +603,10 @@ func TestMuxServerGetMetadata(t *testing.T) { t.Errorf("functions didn't match expectations: %s", diff) } + if diff := cmp.Diff(resp.EphemeralResources, testCase.expectedEphemeralResources); diff != "" { + t.Errorf("ephemeral resources didn't match expectations: %s", diff) + } + if diff := cmp.Diff(resp.Resources, testCase.expectedResources); diff != "" { t.Errorf("resources didn't match expectations: %s", diff) } diff --git a/tf6muxserver/mux_server_GetProviderSchema.go b/tf6muxserver/mux_server_GetProviderSchema.go index e725dcc..4d0db7d 100644 --- a/tf6muxserver/mux_server_GetProviderSchema.go +++ b/tf6muxserver/mux_server_GetProviderSchema.go @@ -25,10 +25,11 @@ func (s *muxServer) GetProviderSchema(ctx context.Context, req *tfprotov6.GetPro defer s.serverDiscoveryMutex.Unlock() resp := &tfprotov6.GetProviderSchemaResponse{ - DataSourceSchemas: make(map[string]*tfprotov6.Schema), - Functions: make(map[string]*tfprotov6.Function), - ResourceSchemas: make(map[string]*tfprotov6.Schema), - ServerCapabilities: serverCapabilities, + DataSourceSchemas: make(map[string]*tfprotov6.Schema), + EphemeralResourceSchemas: make(map[string]*tfprotov6.Schema), + Functions: make(map[string]*tfprotov6.Function), + ResourceSchemas: make(map[string]*tfprotov6.Schema), + ServerCapabilities: serverCapabilities, } for _, server := range s.servers { @@ -106,6 +107,17 @@ func (s *muxServer) GetProviderSchema(ctx context.Context, req *tfprotov6.GetPro s.functions[name] = server resp.Functions[name] = definition } + + for ephemeralResourceType, schema := range serverResp.EphemeralResourceSchemas { + if _, ok := resp.EphemeralResourceSchemas[ephemeralResourceType]; ok { + resp.Diagnostics = append(resp.Diagnostics, ephemeralResourceDuplicateError(ephemeralResourceType)) + + continue + } + + s.ephemeralResources[ephemeralResourceType] = server + resp.EphemeralResourceSchemas[ephemeralResourceType] = schema + } } s.serverDiscoveryComplete = true diff --git a/tf6muxserver/mux_server_GetProviderSchema_test.go b/tf6muxserver/mux_server_GetProviderSchema_test.go index 8276fbf..346ba27 100644 --- a/tf6muxserver/mux_server_GetProviderSchema_test.go +++ b/tf6muxserver/mux_server_GetProviderSchema_test.go @@ -19,14 +19,15 @@ func TestMuxServerGetProviderSchema(t *testing.T) { t.Parallel() testCases := map[string]struct { - servers []func() tfprotov6.ProviderServer - expectedDataSourceSchemas map[string]*tfprotov6.Schema - expectedDiagnostics []*tfprotov6.Diagnostic - expectedFunctions map[string]*tfprotov6.Function - expectedProviderSchema *tfprotov6.Schema - expectedProviderMetaSchema *tfprotov6.Schema - expectedResourceSchemas map[string]*tfprotov6.Schema - expectedServerCapabilities *tfprotov6.ServerCapabilities + servers []func() tfprotov6.ProviderServer + expectedDataSourceSchemas map[string]*tfprotov6.Schema + expectedDiagnostics []*tfprotov6.Diagnostic + expectedEphemeralResourcesSchemas map[string]*tfprotov6.Schema + expectedFunctions map[string]*tfprotov6.Function + expectedProviderSchema *tfprotov6.Schema + expectedProviderMetaSchema *tfprotov6.Schema + expectedResourceSchemas map[string]*tfprotov6.Schema + expectedServerCapabilities *tfprotov6.ServerCapabilities }{ "combined": { servers: []func() tfprotov6.ProviderServer{ @@ -152,6 +153,45 @@ func TestMuxServerGetProviderSchema(t *testing.T) { }, }, }, + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_foo": { + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "secret_number", + Type: tftypes.Number, + Required: true, + Description: "input the secret number", + DescriptionKind: tfprotov6.StringKindPlain, + }, + }, + }, + }, + "test_ephemeral_bar": { + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "username", + Type: tftypes.String, + Optional: true, + Description: "your username", + DescriptionKind: tfprotov6.StringKindPlain, + }, + { + Name: "password", + Type: tftypes.String, + Optional: true, + Description: "your password", + DescriptionKind: tfprotov6.StringKindPlain, + }, + }, + }, + }, + }, }, }).ProviderServer, (&tf6testserver.TestServer{ @@ -279,6 +319,23 @@ func TestMuxServerGetProviderSchema(t *testing.T) { }, }, }, + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_foobar": { + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "secret_number", + Type: tftypes.Number, + Computed: true, + Description: "A generated secret number", + DescriptionKind: tfprotov6.StringKindPlain, + }, + }, + }, + }, + }, }, }).ProviderServer, }, @@ -462,6 +519,60 @@ func TestMuxServerGetProviderSchema(t *testing.T) { }, }, }, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_foo": { + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "secret_number", + Type: tftypes.Number, + Required: true, + Description: "input the secret number", + DescriptionKind: tfprotov6.StringKindPlain, + }, + }, + }, + }, + "test_ephemeral_bar": { + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "username", + Type: tftypes.String, + Optional: true, + Description: "your username", + DescriptionKind: tfprotov6.StringKindPlain, + }, + { + Name: "password", + Type: tftypes.String, + Optional: true, + Description: "your password", + DescriptionKind: tfprotov6.StringKindPlain, + }, + }, + }, + }, + "test_ephemeral_foobar": { + Version: 1, + Block: &tfprotov6.SchemaBlock{ + Version: 1, + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "secret_number", + Type: tftypes.Number, + Computed: true, + Description: "A generated secret number", + DescriptionKind: tfprotov6.StringKindPlain, + }, + }, + }, + }, + }, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -498,6 +609,46 @@ func TestMuxServerGetProviderSchema(t *testing.T) { "Duplicate data source type: test_foo", }, }, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, + expectedResourceSchemas: map[string]*tfprotov6.Schema{}, + expectedServerCapabilities: &tfprotov6.ServerCapabilities{ + GetProviderSchemaOptional: true, + MoveResourceState: true, + PlanDestroy: true, + }, + }, + "duplicate-ephemeral-resource-type": { + servers: []func() tfprotov6.ProviderServer{ + (&tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_foo": {}, + }, + }, + }).ProviderServer, + (&tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_foo": {}, + }, + }, + }).ProviderServer, + }, + expectedDataSourceSchemas: map[string]*tfprotov6.Schema{}, + expectedDiagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Invalid Provider Server Combination", + Detail: "The combined provider has multiple implementations of the same ephemeral resource type across underlying providers. " + + "Ephemeral resource types must be implemented by only one underlying provider. " + + "This is always an issue in the provider implementation and should be reported to the provider developers.\n\n" + + "Duplicate ephemeral resource type: test_foo", + }, + }, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{ + "test_foo": {}, + }, expectedFunctions: map[string]*tfprotov6.Function{}, expectedResourceSchemas: map[string]*tfprotov6.Schema{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ @@ -534,6 +685,7 @@ func TestMuxServerGetProviderSchema(t *testing.T) { "Duplicate function: test_function", }, }, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, expectedFunctions: map[string]*tfprotov6.Function{ "test_function": {}, }, @@ -572,7 +724,8 @@ func TestMuxServerGetProviderSchema(t *testing.T) { "Duplicate resource type: test_foo", }, }, - expectedFunctions: map[string]*tfprotov6.Function{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, expectedResourceSchemas: map[string]*tfprotov6.Schema{ "test_foo": {}, }, @@ -649,7 +802,8 @@ func TestMuxServerGetProviderSchema(t *testing.T) { ), }, }, - expectedFunctions: map[string]*tfprotov6.Function{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, expectedProviderSchema: &tfprotov6.Schema{ Block: &tfprotov6.SchemaBlock{ Attributes: []*tfprotov6.SchemaAttribute{ @@ -735,7 +889,8 @@ func TestMuxServerGetProviderSchema(t *testing.T) { ), }, }, - expectedFunctions: map[string]*tfprotov6.Function{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, expectedProviderMetaSchema: &tfprotov6.Schema{ Block: &tfprotov6.SchemaBlock{ Attributes: []*tfprotov6.SchemaAttribute{ @@ -762,7 +917,8 @@ func TestMuxServerGetProviderSchema(t *testing.T) { "test_with_server_capabilities": {}, }, ServerCapabilities: &tfprotov6.ServerCapabilities{ - PlanDestroy: true, + GetProviderSchemaOptional: true, + PlanDestroy: true, }, }, }).ProviderServer, @@ -774,8 +930,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { }, }).ProviderServer, }, - expectedDataSourceSchemas: map[string]*tfprotov6.Schema{}, - expectedFunctions: map[string]*tfprotov6.Function{}, + expectedDataSourceSchemas: map[string]*tfprotov6.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, expectedResourceSchemas: map[string]*tfprotov6.Schema{ "test_with_server_capabilities": {}, "test_without_server_capabilities": {}, @@ -810,8 +967,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: map[string]*tfprotov6.Function{}, - expectedResourceSchemas: map[string]*tfprotov6.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, + expectedResourceSchemas: map[string]*tfprotov6.Schema{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -857,8 +1015,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: map[string]*tfprotov6.Function{}, - expectedResourceSchemas: map[string]*tfprotov6.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, + expectedResourceSchemas: map[string]*tfprotov6.Schema{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -889,8 +1048,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test warning details", }, }, - expectedFunctions: map[string]*tfprotov6.Function{}, - expectedResourceSchemas: map[string]*tfprotov6.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, + expectedResourceSchemas: map[string]*tfprotov6.Schema{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -936,8 +1096,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test warning details", }, }, - expectedFunctions: map[string]*tfprotov6.Function{}, - expectedResourceSchemas: map[string]*tfprotov6.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, + expectedResourceSchemas: map[string]*tfprotov6.Schema{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -983,8 +1144,9 @@ func TestMuxServerGetProviderSchema(t *testing.T) { Detail: "test error details", }, }, - expectedFunctions: map[string]*tfprotov6.Function{}, - expectedResourceSchemas: map[string]*tfprotov6.Schema{}, + expectedEphemeralResourcesSchemas: map[string]*tfprotov6.Schema{}, + expectedFunctions: map[string]*tfprotov6.Function{}, + expectedResourceSchemas: map[string]*tfprotov6.Schema{}, expectedServerCapabilities: &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -1019,6 +1181,10 @@ func TestMuxServerGetProviderSchema(t *testing.T) { t.Errorf("diagnostics didn't match expectations: %s", diff) } + if diff := cmp.Diff(resp.EphemeralResourceSchemas, testCase.expectedEphemeralResourcesSchemas); diff != "" { + t.Errorf("ephemeral resources schemas didn't match expectations: %s", diff) + } + if diff := cmp.Diff(resp.Functions, testCase.expectedFunctions); diff != "" { t.Errorf("functions didn't match expectations: %s", diff) } diff --git a/tf6muxserver/mux_server_OpenEphemeralResource.go b/tf6muxserver/mux_server_OpenEphemeralResource.go new file mode 100644 index 0000000..ff54ff1 --- /dev/null +++ b/tf6muxserver/mux_server_OpenEphemeralResource.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf6muxserver + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-mux/internal/logging" +) + +func (s *muxServer) OpenEphemeralResource(ctx context.Context, req *tfprotov6.OpenEphemeralResourceRequest) (*tfprotov6.OpenEphemeralResourceResponse, error) { + rpc := "OpenEphemeralResource" + ctx = logging.InitContext(ctx) + ctx = logging.RpcContext(ctx, rpc) + + server, diags, err := s.getEphemeralResourceServer(ctx, req.TypeName) + + if err != nil { + return nil, err + } + + if diagnosticsHasError(diags) { + return &tfprotov6.OpenEphemeralResourceResponse{ + Diagnostics: diags, + }, nil + } + + ctx = logging.Tfprotov6ProviderServerContext(ctx, server) + logging.MuxTrace(ctx, "calling downstream server") + + return server.OpenEphemeralResource(ctx, req) +} diff --git a/tf6muxserver/mux_server_OpenEphemeralResource_test.go b/tf6muxserver/mux_server_OpenEphemeralResource_test.go new file mode 100644 index 0000000..80ccd5f --- /dev/null +++ b/tf6muxserver/mux_server_OpenEphemeralResource_test.go @@ -0,0 +1,72 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf6muxserver_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-mux/internal/tf6testserver" + "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" +) + +func TestMuxServerOpenEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testServer1 := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource_server1": {}, + }, + }, + } + testServer2 := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource_server2": {}, + }, + }, + } + servers := []func() tfprotov6.ProviderServer{testServer1.ProviderServer, testServer2.ProviderServer} + muxServer, err := tf6muxserver.NewMuxServer(ctx, servers...) + + if err != nil { + t.Fatalf("unexpected error setting up factory: %s", err) + } + + _, err = muxServer.ProviderServer().OpenEphemeralResource(ctx, &tfprotov6.OpenEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server1", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !testServer1.OpenEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("expected test_ephemeral_resource_server1 OpenEphemeralResource to be called on server1") + } + + if testServer2.OpenEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("unexpected test_ephemeral_resource_server1 OpenEphemeralResource called on server2") + } + + _, err = muxServer.ProviderServer().OpenEphemeralResource(ctx, &tfprotov6.OpenEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server2", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if testServer1.OpenEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("unexpected test_ephemeral_resource_server2 OpenEphemeralResource called on server1") + } + + if !testServer2.OpenEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("expected test_ephemeral_resource_server2 OpenEphemeralResource to be called on server2") + } +} diff --git a/tf6muxserver/mux_server_RenewEphemeralResource.go b/tf6muxserver/mux_server_RenewEphemeralResource.go new file mode 100644 index 0000000..26156dc --- /dev/null +++ b/tf6muxserver/mux_server_RenewEphemeralResource.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf6muxserver + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-mux/internal/logging" +) + +func (s *muxServer) RenewEphemeralResource(ctx context.Context, req *tfprotov6.RenewEphemeralResourceRequest) (*tfprotov6.RenewEphemeralResourceResponse, error) { + rpc := "RenewEphemeralResource" + ctx = logging.InitContext(ctx) + ctx = logging.RpcContext(ctx, rpc) + + server, diags, err := s.getEphemeralResourceServer(ctx, req.TypeName) + + if err != nil { + return nil, err + } + + if diagnosticsHasError(diags) { + return &tfprotov6.RenewEphemeralResourceResponse{ + Diagnostics: diags, + }, nil + } + + ctx = logging.Tfprotov6ProviderServerContext(ctx, server) + logging.MuxTrace(ctx, "calling downstream server") + + return server.RenewEphemeralResource(ctx, req) +} diff --git a/tf6muxserver/mux_server_RenewEphemeralResource_test.go b/tf6muxserver/mux_server_RenewEphemeralResource_test.go new file mode 100644 index 0000000..090f6cf --- /dev/null +++ b/tf6muxserver/mux_server_RenewEphemeralResource_test.go @@ -0,0 +1,72 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf6muxserver_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-mux/internal/tf6testserver" + "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" +) + +func TestMuxServerRenewEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testServer1 := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource_server1": {}, + }, + }, + } + testServer2 := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource_server2": {}, + }, + }, + } + servers := []func() tfprotov6.ProviderServer{testServer1.ProviderServer, testServer2.ProviderServer} + muxServer, err := tf6muxserver.NewMuxServer(ctx, servers...) + + if err != nil { + t.Fatalf("unexpected error setting up factory: %s", err) + } + + _, err = muxServer.ProviderServer().RenewEphemeralResource(ctx, &tfprotov6.RenewEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server1", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !testServer1.RenewEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("expected test_ephemeral_resource_server1 RenewEphemeralResource to be called on server1") + } + + if testServer2.RenewEphemeralResourceCalled["test_ephemeral_resource_server1"] { + t.Errorf("unexpected test_ephemeral_resource_server1 RenewEphemeralResource called on server2") + } + + _, err = muxServer.ProviderServer().RenewEphemeralResource(ctx, &tfprotov6.RenewEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource_server2", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if testServer1.RenewEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("unexpected test_ephemeral_resource_server2 RenewEphemeralResource called on server1") + } + + if !testServer2.RenewEphemeralResourceCalled["test_ephemeral_resource_server2"] { + t.Errorf("expected test_ephemeral_resource_server2 RenewEphemeralResource to be called on server2") + } +} diff --git a/tf6muxserver/mux_server_ValidateEphemeralResourceConfig.go b/tf6muxserver/mux_server_ValidateEphemeralResourceConfig.go new file mode 100644 index 0000000..aac483d --- /dev/null +++ b/tf6muxserver/mux_server_ValidateEphemeralResourceConfig.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf6muxserver + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-mux/internal/logging" +) + +func (s *muxServer) ValidateEphemeralResourceConfig(ctx context.Context, req *tfprotov6.ValidateEphemeralResourceConfigRequest) (*tfprotov6.ValidateEphemeralResourceConfigResponse, error) { + rpc := "ValidateEphemeralResourceTypeConfig" + ctx = logging.InitContext(ctx) + ctx = logging.RpcContext(ctx, rpc) + + server, diags, err := s.getEphemeralResourceServer(ctx, req.TypeName) + + if err != nil { + return nil, err + } + + if diagnosticsHasError(diags) { + return &tfprotov6.ValidateEphemeralResourceConfigResponse{ + Diagnostics: diags, + }, nil + } + + ctx = logging.Tfprotov6ProviderServerContext(ctx, server) + logging.MuxTrace(ctx, "calling downstream server") + + return server.ValidateEphemeralResourceConfig(ctx, req) +} diff --git a/tf6muxserver/mux_server_ValidateEphemeralResourceConfig_test.go b/tf6muxserver/mux_server_ValidateEphemeralResourceConfig_test.go new file mode 100644 index 0000000..8df2240 --- /dev/null +++ b/tf6muxserver/mux_server_ValidateEphemeralResourceConfig_test.go @@ -0,0 +1,72 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tf6muxserver_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-mux/internal/tf6testserver" + "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" +) + +func TestMuxServerValidateEphemeralResourceConfig(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testServer1 := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource_server1": {}, + }, + }, + } + testServer2 := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource_server2": {}, + }, + }, + } + servers := []func() tfprotov6.ProviderServer{testServer1.ProviderServer, testServer2.ProviderServer} + muxServer, err := tf6muxserver.NewMuxServer(ctx, servers...) + + if err != nil { + t.Fatalf("unexpected error setting up factory: %s", err) + } + + _, err = muxServer.ProviderServer().ValidateEphemeralResourceConfig(ctx, &tfprotov6.ValidateEphemeralResourceConfigRequest{ + TypeName: "test_ephemeral_resource_server1", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !testServer1.ValidateEphemeralResourceConfigCalled["test_ephemeral_resource_server1"] { + t.Errorf("expected test_ephemeral_resource_server1 ValidateEphemeralResourceConfig to be called on server1") + } + + if testServer2.ValidateEphemeralResourceConfigCalled["test_ephemeral_resource_server1"] { + t.Errorf("unexpected test_ephemeral_resource_server1 ValidateEphemeralResourceConfig called on server2") + } + + _, err = muxServer.ProviderServer().ValidateEphemeralResourceConfig(ctx, &tfprotov6.ValidateEphemeralResourceConfigRequest{ + TypeName: "test_ephemeral_resource_server2", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if testServer1.ValidateEphemeralResourceConfigCalled["test_ephemeral_resource_server2"] { + t.Errorf("unexpected test_ephemeral_resource_server2 ValidateEphemeralResourceConfig called on server1") + } + + if !testServer2.ValidateEphemeralResourceConfigCalled["test_ephemeral_resource_server2"] { + t.Errorf("expected test_ephemeral_resource_server2 ValidateEphemeralResourceConfig to be called on server2") + } +} diff --git a/tf6to5server/tf6to5server.go b/tf6to5server/tf6to5server.go index ccd7ced..c4dcd2d 100644 --- a/tf6to5server/tf6to5server.go +++ b/tf6to5server/tf6to5server.go @@ -67,6 +67,17 @@ func (s v6tov5Server) CallFunction(ctx context.Context, req *tfprotov5.CallFunct return tfprotov6tov5.CallFunctionResponse(v6Resp), nil } +func (s v6tov5Server) CloseEphemeralResource(ctx context.Context, req *tfprotov5.CloseEphemeralResourceRequest) (*tfprotov5.CloseEphemeralResourceResponse, error) { + v6Req := tfprotov5tov6.CloseEphemeralResourceRequest(req) + + v6Resp, err := s.v6Server.CloseEphemeralResource(ctx, v6Req) + if err != nil { + return nil, err + } + + return tfprotov6tov5.CloseEphemeralResourceResponse(v6Resp), nil +} + func (s v6tov5Server) ConfigureProvider(ctx context.Context, req *tfprotov5.ConfigureProviderRequest) (*tfprotov5.ConfigureProviderResponse, error) { v6Req := tfprotov5tov6.ConfigureProviderRequest(req) v6Resp, err := s.v6Server.ConfigureProvider(ctx, v6Req) @@ -133,6 +144,17 @@ func (s v6tov5Server) MoveResourceState(ctx context.Context, req *tfprotov5.Move return tfprotov6tov5.MoveResourceStateResponse(v6Resp), nil } +func (s v6tov5Server) OpenEphemeralResource(ctx context.Context, req *tfprotov5.OpenEphemeralResourceRequest) (*tfprotov5.OpenEphemeralResourceResponse, error) { + v6Req := tfprotov5tov6.OpenEphemeralResourceRequest(req) + + v6Resp, err := s.v6Server.OpenEphemeralResource(ctx, v6Req) + if err != nil { + return nil, err + } + + return tfprotov6tov5.OpenEphemeralResourceResponse(v6Resp), nil +} + func (s v6tov5Server) PlanResourceChange(ctx context.Context, req *tfprotov5.PlanResourceChangeRequest) (*tfprotov5.PlanResourceChangeResponse, error) { v6Req := tfprotov5tov6.PlanResourceChangeRequest(req) v6Resp, err := s.v6Server.PlanResourceChange(ctx, v6Req) @@ -182,6 +204,17 @@ func (s v6tov5Server) ReadResource(ctx context.Context, req *tfprotov5.ReadResou return tfprotov6tov5.ReadResourceResponse(v6Resp), nil } +func (s v6tov5Server) RenewEphemeralResource(ctx context.Context, req *tfprotov5.RenewEphemeralResourceRequest) (*tfprotov5.RenewEphemeralResourceResponse, error) { + v6Req := tfprotov5tov6.RenewEphemeralResourceRequest(req) + + v6Resp, err := s.v6Server.RenewEphemeralResource(ctx, v6Req) + if err != nil { + return nil, err + } + + return tfprotov6tov5.RenewEphemeralResourceResponse(v6Resp), nil +} + func (s v6tov5Server) StopProvider(ctx context.Context, req *tfprotov5.StopProviderRequest) (*tfprotov5.StopProviderResponse, error) { v6Req := tfprotov5tov6.StopProviderRequest(req) v6Resp, err := s.v6Server.StopProvider(ctx, v6Req) @@ -215,6 +248,17 @@ func (s v6tov5Server) ValidateDataSourceConfig(ctx context.Context, req *tfproto return tfprotov6tov5.ValidateDataSourceConfigResponse(v6Resp), nil } +func (s v6tov5Server) ValidateEphemeralResourceConfig(ctx context.Context, req *tfprotov5.ValidateEphemeralResourceConfigRequest) (*tfprotov5.ValidateEphemeralResourceConfigResponse, error) { + v6Req := tfprotov5tov6.ValidateEphemeralResourceConfigRequest(req) + v6Resp, err := s.v6Server.ValidateEphemeralResourceConfig(ctx, v6Req) + + if err != nil { + return nil, err + } + + return tfprotov6tov5.ValidateEphemeralResourceConfigResponse(v6Resp), nil +} + func (s v6tov5Server) ValidateResourceTypeConfig(ctx context.Context, req *tfprotov5.ValidateResourceTypeConfigRequest) (*tfprotov5.ValidateResourceTypeConfigResponse, error) { v6Req := tfprotov5tov6.ValidateResourceConfigRequest(req) v6Resp, err := s.v6Server.ValidateResourceConfig(ctx, v6Req) diff --git a/tf6to5server/tf6to5server_test.go b/tf6to5server/tf6to5server_test.go index 2316386..4977d8f 100644 --- a/tf6to5server/tf6to5server_test.go +++ b/tf6to5server/tf6to5server_test.go @@ -31,6 +31,9 @@ func TestDowngradeServer(t *testing.T) { DataSourceSchemas: map[string]*tfprotov6.Schema{ "test_data_source": {}, }, + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource": {}, + }, Functions: map[string]*tfprotov6.Function{ "test_function": {}, }, @@ -289,6 +292,37 @@ func TestV6ToV5ServerCallFunction(t *testing.T) { } } +func TestV6ToV5ServerCloseEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + v6server := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + ResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource": {}, + }, + }, + } + + v5server, err := tf6to5server.DowngradeServer(context.Background(), v6server.ProviderServer) + + if err != nil { + t.Fatalf("unexpected error downgrading server: %s", err) + } + + _, err = v5server.CloseEphemeralResource(ctx, &tfprotov5.CloseEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !v6server.CloseEphemeralResourceCalled["test_ephemeral_resource"] { + t.Errorf("expected test_ephemeral_resource CloseEphemeralResource to be called") + } +} + func TestV6ToV5ServerConfigureProvider(t *testing.T) { t.Parallel() @@ -469,6 +503,37 @@ func TestV6ToV5ServerMoveResourceState(t *testing.T) { } } +func TestV6ToV5ServerOpenEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + v6server := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + ResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource": {}, + }, + }, + } + + v5server, err := tf6to5server.DowngradeServer(context.Background(), v6server.ProviderServer) + + if err != nil { + t.Fatalf("unexpected error downgrading server: %s", err) + } + + _, err = v5server.OpenEphemeralResource(ctx, &tfprotov5.OpenEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !v6server.OpenEphemeralResourceCalled["test_ephemeral_resource"] { + t.Errorf("expected test_ephemeral_resource OpenEphemeralResource to be called") + } +} + func TestV6ToV5ServerPlanResourceChange(t *testing.T) { t.Parallel() @@ -591,6 +656,37 @@ func TestV6ToV5ServerReadResource(t *testing.T) { } } +func TestV6ToV5ServerRenewEphemeralResource(t *testing.T) { + t.Parallel() + + ctx := context.Background() + v6server := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + ResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource": {}, + }, + }, + } + + v5server, err := tf6to5server.DowngradeServer(context.Background(), v6server.ProviderServer) + + if err != nil { + t.Fatalf("unexpected error downgrading server: %s", err) + } + + _, err = v5server.RenewEphemeralResource(ctx, &tfprotov5.RenewEphemeralResourceRequest{ + TypeName: "test_ephemeral_resource", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !v6server.RenewEphemeralResourceCalled["test_ephemeral_resource"] { + t.Errorf("expected test_ephemeral_resource RenewEphemeralResource to be called") + } +} + func TestV6ToV5ServerStopProvider(t *testing.T) { t.Parallel() @@ -682,6 +778,37 @@ func TestV6ToV5ServerValidateDataSourceConfig(t *testing.T) { } } +func TestV6ToV5ServerValidateEphemeralResourceConfig(t *testing.T) { + t.Parallel() + + ctx := context.Background() + v6server := &tf6testserver.TestServer{ + GetProviderSchemaResponse: &tfprotov6.GetProviderSchemaResponse{ + ResourceSchemas: map[string]*tfprotov6.Schema{ + "test_ephemeral_resource": {}, + }, + }, + } + + v5server, err := tf6to5server.DowngradeServer(context.Background(), v6server.ProviderServer) + + if err != nil { + t.Fatalf("unexpected error downgrading server: %s", err) + } + + _, err = v5server.ValidateEphemeralResourceConfig(ctx, &tfprotov5.ValidateEphemeralResourceConfigRequest{ + TypeName: "test_ephemeral_resource", + }) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !v6server.ValidateEphemeralResourceConfigCalled["test_ephemeral_resource"] { + t.Errorf("expected test_ephemeral_resource ValidateEphemeralResourceConfig to be called") + } +} + func TestV6ToV5ServerValidateResourceTypeConfig(t *testing.T) { t.Parallel() From e68d049036abb06881f0ca5905fec5ecffa92863 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 27 Aug 2024 14:40:09 -0400 Subject: [PATCH 03/10] Remove `State` field from `RenewEphemeralResource` RPC response and rename `PriorState` request fields to `State`. --- go.mod | 2 +- go.sum | 2 ++ internal/tfprotov5tov6/tfprotov5tov6.go | 15 ++++++----- internal/tfprotov5tov6/tfprotov5tov6_test.go | 26 +++++++++----------- internal/tfprotov6tov5/tfprotov6tov5.go | 15 ++++++----- internal/tfprotov6tov5/tfprotov6tov5_test.go | 26 +++++++++----------- 6 files changed, 41 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index 2f466e7..40eaa0b 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.6 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240819181557-bbf46acb5208 + github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240827183355-145d7bc53a3f github.com/hashicorp/terraform-plugin-log v0.9.0 google.golang.org/grpc v1.65.0 ) diff --git a/go.sum b/go.sum index 3c312b9..d3edf79 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240819181557-bbf46acb5208 h1:ky9zy4izz4XTn0uW3aSls6aiQdRYBCFoHPPsH8+fSFI= github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240819181557-bbf46acb5208/go.mod h1:ko0HcPe7AkwMukddKa13Qq5zyvi8V9KYxBZmqU8a9cE= +github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240827183355-145d7bc53a3f h1:6E4kyvCnk1Vb0L/wEfvBZFF4vkkbvpsOOHoRkDu60I8= +github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240827183355-145d7bc53a3f/go.mod h1:ko0HcPe7AkwMukddKa13Qq5zyvi8V9KYxBZmqU8a9cE= 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.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= diff --git a/internal/tfprotov5tov6/tfprotov5tov6.go b/internal/tfprotov5tov6/tfprotov5tov6.go index 2c69b60..87ddb04 100644 --- a/internal/tfprotov5tov6/tfprotov5tov6.go +++ b/internal/tfprotov5tov6/tfprotov5tov6.go @@ -70,9 +70,9 @@ func CloseEphemeralResourceRequest(in *tfprotov5.CloseEphemeralResourceRequest) } return &tfprotov6.CloseEphemeralResourceRequest{ - TypeName: in.TypeName, - PriorState: DynamicValue(in.PriorState), - Private: in.Private, + TypeName: in.TypeName, + State: DynamicValue(in.State), + Private: in.Private, } } @@ -606,10 +606,10 @@ func RenewEphemeralResourceRequest(in *tfprotov5.RenewEphemeralResourceRequest) } return &tfprotov6.RenewEphemeralResourceRequest{ - TypeName: in.TypeName, - Config: DynamicValue(in.Config), - PriorState: DynamicValue(in.PriorState), - Private: in.Private, + TypeName: in.TypeName, + Config: DynamicValue(in.Config), + State: DynamicValue(in.State), + Private: in.Private, } } @@ -619,7 +619,6 @@ func RenewEphemeralResourceResponse(in *tfprotov5.RenewEphemeralResourceResponse } return &tfprotov6.RenewEphemeralResourceResponse{ - State: DynamicValue(in.State), Diagnostics: Diagnostics(in.Diagnostics), Private: in.Private, RenewAt: in.RenewAt, diff --git a/internal/tfprotov5tov6/tfprotov5tov6_test.go b/internal/tfprotov5tov6/tfprotov5tov6_test.go index 71ab38d..449fcad 100644 --- a/internal/tfprotov5tov6/tfprotov5tov6_test.go +++ b/internal/tfprotov5tov6/tfprotov5tov6_test.go @@ -306,14 +306,14 @@ func TestCloseEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov5.CloseEphemeralResourceRequest{ - PriorState: &testTfprotov5DynamicValue, - Private: testBytes, - TypeName: "test_ephemeral_resource", + State: &testTfprotov5DynamicValue, + Private: testBytes, + TypeName: "test_ephemeral_resource", }, expected: &tfprotov6.CloseEphemeralResourceRequest{ - PriorState: &testTfprotov6DynamicValue, - Private: testBytes, - TypeName: "test_ephemeral_resource", + State: &testTfprotov6DynamicValue, + Private: testBytes, + TypeName: "test_ephemeral_resource", }, }, } @@ -1799,14 +1799,14 @@ func TestRenewEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov5.RenewEphemeralResourceRequest{ - Config: &testTfprotov5DynamicValue, - PriorState: &testTfprotov5DynamicValue, - TypeName: "test_ephemeral_resource", + Config: &testTfprotov5DynamicValue, + State: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", }, expected: &tfprotov6.RenewEphemeralResourceRequest{ - Config: &testTfprotov6DynamicValue, - PriorState: &testTfprotov6DynamicValue, - TypeName: "test_ephemeral_resource", + Config: &testTfprotov6DynamicValue, + State: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", }, }, } @@ -1842,13 +1842,11 @@ func TestRenewEphemeralResourceResponse(t *testing.T) { Diagnostics: testTfprotov5Diagnostics, Private: testBytes, RenewAt: testTime, - State: &testTfprotov5DynamicValue, }, expected: &tfprotov6.RenewEphemeralResourceResponse{ Diagnostics: testTfprotov6Diagnostics, Private: testBytes, RenewAt: testTime, - State: &testTfprotov6DynamicValue, }, }, } diff --git a/internal/tfprotov6tov5/tfprotov6tov5.go b/internal/tfprotov6tov5/tfprotov6tov5.go index 21eb2dc..c17d2fb 100644 --- a/internal/tfprotov6tov5/tfprotov6tov5.go +++ b/internal/tfprotov6tov5/tfprotov6tov5.go @@ -75,9 +75,9 @@ func CloseEphemeralResourceRequest(in *tfprotov6.CloseEphemeralResourceRequest) } return &tfprotov5.CloseEphemeralResourceRequest{ - TypeName: in.TypeName, - PriorState: DynamicValue(in.PriorState), - Private: in.Private, + TypeName: in.TypeName, + State: DynamicValue(in.State), + Private: in.Private, } } @@ -660,10 +660,10 @@ func RenewEphemeralResourceRequest(in *tfprotov6.RenewEphemeralResourceRequest) } return &tfprotov5.RenewEphemeralResourceRequest{ - TypeName: in.TypeName, - Config: DynamicValue(in.Config), - PriorState: DynamicValue(in.PriorState), - Private: in.Private, + TypeName: in.TypeName, + Config: DynamicValue(in.Config), + State: DynamicValue(in.State), + Private: in.Private, } } @@ -673,7 +673,6 @@ func RenewEphemeralResourceResponse(in *tfprotov6.RenewEphemeralResourceResponse } return &tfprotov5.RenewEphemeralResourceResponse{ - State: DynamicValue(in.State), Diagnostics: Diagnostics(in.Diagnostics), Private: in.Private, RenewAt: in.RenewAt, diff --git a/internal/tfprotov6tov5/tfprotov6tov5_test.go b/internal/tfprotov6tov5/tfprotov6tov5_test.go index 3a3eae1..437b39f 100644 --- a/internal/tfprotov6tov5/tfprotov6tov5_test.go +++ b/internal/tfprotov6tov5/tfprotov6tov5_test.go @@ -308,14 +308,14 @@ func TestCloseEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov6.CloseEphemeralResourceRequest{ - PriorState: &testTfprotov6DynamicValue, - Private: testBytes, - TypeName: "test_ephemeral_resource", + State: &testTfprotov6DynamicValue, + Private: testBytes, + TypeName: "test_ephemeral_resource", }, expected: &tfprotov5.CloseEphemeralResourceRequest{ - PriorState: &testTfprotov5DynamicValue, - Private: testBytes, - TypeName: "test_ephemeral_resource", + State: &testTfprotov5DynamicValue, + Private: testBytes, + TypeName: "test_ephemeral_resource", }, }, } @@ -2014,14 +2014,14 @@ func TestRenewEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov6.RenewEphemeralResourceRequest{ - Config: &testTfprotov6DynamicValue, - PriorState: &testTfprotov6DynamicValue, - TypeName: "test_ephemeral_resource", + Config: &testTfprotov6DynamicValue, + State: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", }, expected: &tfprotov5.RenewEphemeralResourceRequest{ - Config: &testTfprotov5DynamicValue, - PriorState: &testTfprotov5DynamicValue, - TypeName: "test_ephemeral_resource", + Config: &testTfprotov5DynamicValue, + State: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", }, }, } @@ -2057,13 +2057,11 @@ func TestRenewEphemeralResourceResponse(t *testing.T) { Diagnostics: testTfprotov6Diagnostics, Private: testBytes, RenewAt: testTime, - State: &testTfprotov6DynamicValue, }, expected: &tfprotov5.RenewEphemeralResourceResponse{ Diagnostics: testTfprotov5Diagnostics, Private: testBytes, RenewAt: testTime, - State: &testTfprotov5DynamicValue, }, }, } From c1b0898848bbb5909fa6dae80aecd4b198678d5c Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Fri, 6 Sep 2024 08:38:01 -0400 Subject: [PATCH 04/10] update mux to use latest plugin go with optional interfaces --- go.mod | 2 +- go.sum | 6 +- .../mux_server_CloseEphemeralResource.go | 19 ++++- .../mux_server_CloseEphemeralResource_test.go | 10 ++- .../mux_server_OpenEphemeralResource.go | 19 ++++- .../mux_server_OpenEphemeralResource_test.go | 10 ++- .../mux_server_RenewEphemeralResource.go | 19 ++++- .../mux_server_RenewEphemeralResource_test.go | 10 ++- ..._server_ValidateEphemeralResourceConfig.go | 19 ++++- ...er_ValidateEphemeralResourceConfig_test.go | 10 ++- tf5to6server/tf5to6server.go | 80 ++++++++++++++++++- tf5to6server/tf5to6server_test.go | 32 +++++++- .../mux_server_CloseEphemeralResource.go | 19 ++++- .../mux_server_CloseEphemeralResource_test.go | 10 ++- .../mux_server_OpenEphemeralResource.go | 19 ++++- .../mux_server_OpenEphemeralResource_test.go | 10 ++- .../mux_server_RenewEphemeralResource.go | 19 ++++- .../mux_server_RenewEphemeralResource_test.go | 10 ++- ..._server_ValidateEphemeralResourceConfig.go | 19 ++++- ...er_ValidateEphemeralResourceConfig_test.go | 10 ++- tf6to5server/tf6to5server.go | 80 ++++++++++++++++++- tf6to5server/tf6to5server_test.go | 32 +++++++- 22 files changed, 419 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index 40eaa0b..29d8703 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.6 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240827183355-145d7bc53a3f + github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240904211550-7ea64adab752 github.com/hashicorp/terraform-plugin-log v0.9.0 google.golang.org/grpc v1.65.0 ) diff --git a/go.sum b/go.sum index d3edf79..3d99403 100644 --- a/go.sum +++ b/go.sum @@ -15,10 +15,8 @@ github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOs github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= 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.23.1-0.20240819181557-bbf46acb5208 h1:ky9zy4izz4XTn0uW3aSls6aiQdRYBCFoHPPsH8+fSFI= -github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240819181557-bbf46acb5208/go.mod h1:ko0HcPe7AkwMukddKa13Qq5zyvi8V9KYxBZmqU8a9cE= -github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240827183355-145d7bc53a3f h1:6E4kyvCnk1Vb0L/wEfvBZFF4vkkbvpsOOHoRkDu60I8= -github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240827183355-145d7bc53a3f/go.mod h1:ko0HcPe7AkwMukddKa13Qq5zyvi8V9KYxBZmqU8a9cE= +github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240904211550-7ea64adab752 h1:KtwYfSreTB6WRoHmYvvPzCAXPaJeKzsCeFNA0ErzLas= +github.com/hashicorp/terraform-plugin-go v0.23.1-0.20240904211550-7ea64adab752/go.mod h1:ko0HcPe7AkwMukddKa13Qq5zyvi8V9KYxBZmqU8a9cE= 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.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= diff --git a/tf5muxserver/mux_server_CloseEphemeralResource.go b/tf5muxserver/mux_server_CloseEphemeralResource.go index e711a55..3023b42 100644 --- a/tf5muxserver/mux_server_CloseEphemeralResource.go +++ b/tf5muxserver/mux_server_CloseEphemeralResource.go @@ -28,8 +28,25 @@ func (s *muxServer) CloseEphemeralResource(ctx context.Context, req *tfprotov5.C }, nil } + // TODO: Remove and call server.CloseEphemeralResource below directly once interface becomes required. + ephemeralResourceServer, ok := server.(tfprotov5.EphemeralResourceServer) + if !ok { + resp := &tfprotov5.CloseEphemeralResourceResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "CloseEphemeralResource Not Implemented", + Detail: "A CloseEphemeralResource call was received by the provider, however the provider does not implement CloseEphemeralResource. " + + "Either upgrade the provider to a version that implements CloseEphemeralResource 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 server.CloseEphemeralResource(ctx, req) + return ephemeralResourceServer.CloseEphemeralResource(ctx, req) } diff --git a/tf5muxserver/mux_server_CloseEphemeralResource_test.go b/tf5muxserver/mux_server_CloseEphemeralResource_test.go index 5fe8049..878fbb8 100644 --- a/tf5muxserver/mux_server_CloseEphemeralResource_test.go +++ b/tf5muxserver/mux_server_CloseEphemeralResource_test.go @@ -38,7 +38,13 @@ func TestMuxServerCloseEphemeralResource(t *testing.T) { t.Fatalf("unexpected error setting up factory: %s", err) } - _, err = muxServer.ProviderServer().CloseEphemeralResource(ctx, &tfprotov5.CloseEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := muxServer.ProviderServer().(tfprotov5.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("muxServer should implement tfprotov5.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.CloseEphemeralResource(ctx, &tfprotov5.CloseEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server1", }) @@ -54,7 +60,7 @@ func TestMuxServerCloseEphemeralResource(t *testing.T) { t.Errorf("unexpected test_ephemeral_resource_server1 CloseEphemeralResource called on server2") } - _, err = muxServer.ProviderServer().CloseEphemeralResource(ctx, &tfprotov5.CloseEphemeralResourceRequest{ + _, err = ephemeralResourceServer.CloseEphemeralResource(ctx, &tfprotov5.CloseEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server2", }) diff --git a/tf5muxserver/mux_server_OpenEphemeralResource.go b/tf5muxserver/mux_server_OpenEphemeralResource.go index e40a2b7..8367f7a 100644 --- a/tf5muxserver/mux_server_OpenEphemeralResource.go +++ b/tf5muxserver/mux_server_OpenEphemeralResource.go @@ -28,8 +28,25 @@ func (s *muxServer) OpenEphemeralResource(ctx context.Context, req *tfprotov5.Op }, nil } + // TODO: Remove and call server.OpenEphemeralResource below directly once interface becomes required. + ephemeralResourceServer, ok := server.(tfprotov5.EphemeralResourceServer) + if !ok { + resp := &tfprotov5.OpenEphemeralResourceResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "OpenEphemeralResource Not Implemented", + Detail: "A OpenEphemeralResource call was received by the provider, however the provider does not implement OpenEphemeralResource. " + + "Either upgrade the provider to a version that implements OpenEphemeralResource 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 server.OpenEphemeralResource(ctx, req) + return ephemeralResourceServer.OpenEphemeralResource(ctx, req) } diff --git a/tf5muxserver/mux_server_OpenEphemeralResource_test.go b/tf5muxserver/mux_server_OpenEphemeralResource_test.go index 970fa99..c3a8371 100644 --- a/tf5muxserver/mux_server_OpenEphemeralResource_test.go +++ b/tf5muxserver/mux_server_OpenEphemeralResource_test.go @@ -38,7 +38,13 @@ func TestMuxServerOpenEphemeralResource(t *testing.T) { t.Fatalf("unexpected error setting up factory: %s", err) } - _, err = muxServer.ProviderServer().OpenEphemeralResource(ctx, &tfprotov5.OpenEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := muxServer.ProviderServer().(tfprotov5.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("muxServer should implement tfprotov5.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.OpenEphemeralResource(ctx, &tfprotov5.OpenEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server1", }) @@ -54,7 +60,7 @@ func TestMuxServerOpenEphemeralResource(t *testing.T) { t.Errorf("unexpected test_ephemeral_resource_server1 OpenEphemeralResource called on server2") } - _, err = muxServer.ProviderServer().OpenEphemeralResource(ctx, &tfprotov5.OpenEphemeralResourceRequest{ + _, err = ephemeralResourceServer.OpenEphemeralResource(ctx, &tfprotov5.OpenEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server2", }) diff --git a/tf5muxserver/mux_server_RenewEphemeralResource.go b/tf5muxserver/mux_server_RenewEphemeralResource.go index bc6bcc6..e5eb9c7 100644 --- a/tf5muxserver/mux_server_RenewEphemeralResource.go +++ b/tf5muxserver/mux_server_RenewEphemeralResource.go @@ -28,8 +28,25 @@ func (s *muxServer) RenewEphemeralResource(ctx context.Context, req *tfprotov5.R }, nil } + // TODO: Remove and call server.RenewEphemeralResource below directly once interface becomes required. + ephemeralResourceServer, ok := server.(tfprotov5.EphemeralResourceServer) + if !ok { + resp := &tfprotov5.RenewEphemeralResourceResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "RenewEphemeralResource Not Implemented", + Detail: "A RenewEphemeralResource call was received by the provider, however the provider does not implement RenewEphemeralResource. " + + "Either upgrade the provider to a version that implements RenewEphemeralResource 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 server.RenewEphemeralResource(ctx, req) + return ephemeralResourceServer.RenewEphemeralResource(ctx, req) } diff --git a/tf5muxserver/mux_server_RenewEphemeralResource_test.go b/tf5muxserver/mux_server_RenewEphemeralResource_test.go index efd7b0b..ef1bc5c 100644 --- a/tf5muxserver/mux_server_RenewEphemeralResource_test.go +++ b/tf5muxserver/mux_server_RenewEphemeralResource_test.go @@ -38,7 +38,13 @@ func TestMuxServerRenewEphemeralResource(t *testing.T) { t.Fatalf("unexpected error setting up factory: %s", err) } - _, err = muxServer.ProviderServer().RenewEphemeralResource(ctx, &tfprotov5.RenewEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := muxServer.ProviderServer().(tfprotov5.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("muxServer should implement tfprotov5.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.RenewEphemeralResource(ctx, &tfprotov5.RenewEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server1", }) @@ -54,7 +60,7 @@ func TestMuxServerRenewEphemeralResource(t *testing.T) { t.Errorf("unexpected test_ephemeral_resource_server1 RenewEphemeralResource called on server2") } - _, err = muxServer.ProviderServer().RenewEphemeralResource(ctx, &tfprotov5.RenewEphemeralResourceRequest{ + _, err = ephemeralResourceServer.RenewEphemeralResource(ctx, &tfprotov5.RenewEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server2", }) diff --git a/tf5muxserver/mux_server_ValidateEphemeralResourceConfig.go b/tf5muxserver/mux_server_ValidateEphemeralResourceConfig.go index 0f7ccf3..1468f7e 100644 --- a/tf5muxserver/mux_server_ValidateEphemeralResourceConfig.go +++ b/tf5muxserver/mux_server_ValidateEphemeralResourceConfig.go @@ -28,8 +28,25 @@ func (s *muxServer) ValidateEphemeralResourceConfig(ctx context.Context, req *tf }, nil } + // TODO: Remove and call server.ValidateEphemeralResourceConfig below directly once interface becomes required. + ephemeralResourceServer, ok := server.(tfprotov5.EphemeralResourceServer) + if !ok { + resp := &tfprotov5.ValidateEphemeralResourceConfigResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "ValidateEphemeralResourceConfig Not Implemented", + Detail: "A ValidateEphemeralResourceConfig call was received by the provider, however the provider does not implement ValidateEphemeralResourceConfig. " + + "Either upgrade the provider to a version that implements ValidateEphemeralResourceConfig 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 server.ValidateEphemeralResourceConfig(ctx, req) + return ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, req) } diff --git a/tf5muxserver/mux_server_ValidateEphemeralResourceConfig_test.go b/tf5muxserver/mux_server_ValidateEphemeralResourceConfig_test.go index 21f0df7..2691869 100644 --- a/tf5muxserver/mux_server_ValidateEphemeralResourceConfig_test.go +++ b/tf5muxserver/mux_server_ValidateEphemeralResourceConfig_test.go @@ -38,7 +38,13 @@ func TestMuxServerValidateEphemeralResourceConfig(t *testing.T) { t.Fatalf("unexpected error setting up factory: %s", err) } - _, err = muxServer.ProviderServer().ValidateEphemeralResourceConfig(ctx, &tfprotov5.ValidateEphemeralResourceConfigRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := muxServer.ProviderServer().(tfprotov5.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("muxServer should implement tfprotov5.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, &tfprotov5.ValidateEphemeralResourceConfigRequest{ TypeName: "test_ephemeral_resource_server1", }) @@ -54,7 +60,7 @@ func TestMuxServerValidateEphemeralResourceConfig(t *testing.T) { t.Errorf("unexpected test_ephemeral_resource_server1 ValidateEphemeralResourceConfig called on server2") } - _, err = muxServer.ProviderServer().ValidateEphemeralResourceConfig(ctx, &tfprotov5.ValidateEphemeralResourceConfigRequest{ + _, err = ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, &tfprotov5.ValidateEphemeralResourceConfigRequest{ TypeName: "test_ephemeral_resource_server2", }) diff --git a/tf5to6server/tf5to6server.go b/tf5to6server/tf5to6server.go index 27f1538..15ee11d 100644 --- a/tf5to6server/tf5to6server.go +++ b/tf5to6server/tf5to6server.go @@ -56,9 +56,27 @@ func (s v5tov6Server) CallFunction(ctx context.Context, req *tfprotov6.CallFunct } func (s v5tov6Server) CloseEphemeralResource(ctx context.Context, req *tfprotov6.CloseEphemeralResourceRequest) (*tfprotov6.CloseEphemeralResourceResponse, error) { + // TODO: Remove and call s.v5Server.CloseEphemeralResource below directly once interface becomes required + ephemeralResourceServer, ok := s.v5Server.(tfprotov5.EphemeralResourceServer) + if !ok { + v6Resp := &tfprotov6.CloseEphemeralResourceResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "CloseEphemeralResource Not Implemented", + Detail: "A CloseEphemeralResource call was received by the provider, however the provider does not implement the RPC. " + + "Either upgrade the provider to a version that implements CloseEphemeralResource or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return v6Resp, nil + } + v5Req := tfprotov6tov5.CloseEphemeralResourceRequest(req) - v5Resp, err := s.v5Server.CloseEphemeralResource(ctx, v5Req) + // v5Resp, err := s.v5Server.CloseEphemeralResource(ctx, v5Req) + v5Resp, err := ephemeralResourceServer.CloseEphemeralResource(ctx, v5Req) if err != nil { return nil, err } @@ -133,9 +151,27 @@ func (s v5tov6Server) MoveResourceState(ctx context.Context, req *tfprotov6.Move } func (s v5tov6Server) OpenEphemeralResource(ctx context.Context, req *tfprotov6.OpenEphemeralResourceRequest) (*tfprotov6.OpenEphemeralResourceResponse, error) { + // TODO: Remove and call s.v5Server.OpenEphemeralResource below directly once interface becomes required + ephemeralResourceServer, ok := s.v5Server.(tfprotov5.EphemeralResourceServer) + if !ok { + v6Resp := &tfprotov6.OpenEphemeralResourceResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "OpenEphemeralResource Not Implemented", + Detail: "A OpenEphemeralResource call was received by the provider, however the provider does not implement the RPC. " + + "Either upgrade the provider to a version that implements OpenEphemeralResource or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return v6Resp, nil + } + v5Req := tfprotov6tov5.OpenEphemeralResourceRequest(req) - v5Resp, err := s.v5Server.OpenEphemeralResource(ctx, v5Req) + // v5Resp, err := s.v5Server.OpenEphemeralResource(ctx, v5Req) + v5Resp, err := ephemeralResourceServer.OpenEphemeralResource(ctx, v5Req) if err != nil { return nil, err } @@ -182,9 +218,27 @@ func (s v5tov6Server) ReadResource(ctx context.Context, req *tfprotov6.ReadResou } func (s v5tov6Server) RenewEphemeralResource(ctx context.Context, req *tfprotov6.RenewEphemeralResourceRequest) (*tfprotov6.RenewEphemeralResourceResponse, error) { + // TODO: Remove and call s.v5Server.RenewEphemeralResource below directly once interface becomes required + ephemeralResourceServer, ok := s.v5Server.(tfprotov5.EphemeralResourceServer) + if !ok { + v6Resp := &tfprotov6.RenewEphemeralResourceResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "RenewEphemeralResource Not Implemented", + Detail: "A RenewEphemeralResource call was received by the provider, however the provider does not implement the RPC. " + + "Either upgrade the provider to a version that implements RenewEphemeralResource or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return v6Resp, nil + } + v5Req := tfprotov6tov5.RenewEphemeralResourceRequest(req) - v5Resp, err := s.v5Server.RenewEphemeralResource(ctx, v5Req) + // v5Resp, err := s.v5Server.RenewEphemeralResource(ctx, v5Req) + v5Resp, err := ephemeralResourceServer.RenewEphemeralResource(ctx, v5Req) if err != nil { return nil, err } @@ -226,9 +280,27 @@ func (s v5tov6Server) ValidateDataResourceConfig(ctx context.Context, req *tfpro } func (s v5tov6Server) ValidateEphemeralResourceConfig(ctx context.Context, req *tfprotov6.ValidateEphemeralResourceConfigRequest) (*tfprotov6.ValidateEphemeralResourceConfigResponse, error) { + // TODO: Remove and call s.v5Server.ValidateEphemeralResourceConfig below directly once interface becomes required + ephemeralResourceServer, ok := s.v5Server.(tfprotov5.EphemeralResourceServer) + if !ok { + v6Resp := &tfprotov6.ValidateEphemeralResourceConfigResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "ValidateEphemeralResourceConfig Not Implemented", + Detail: "A ValidateEphemeralResourceConfig call was received by the provider, however the provider does not implement the RPC. " + + "Either upgrade the provider to a version that implements ValidateEphemeralResourceConfig or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return v6Resp, nil + } + v5Req := tfprotov6tov5.ValidateEphemeralResourceConfigRequest(req) - v5Resp, err := s.v5Server.ValidateEphemeralResourceConfig(ctx, v5Req) + // v5Resp, err := s.v5Server.ValidateEphemeralResourceConfig(ctx, v5Req) + v5Resp, err := ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, v5Req) if err != nil { return nil, err } diff --git a/tf5to6server/tf5to6server_test.go b/tf5to6server/tf5to6server_test.go index d0f32ec..b7cdcb6 100644 --- a/tf5to6server/tf5to6server_test.go +++ b/tf5to6server/tf5to6server_test.go @@ -212,7 +212,13 @@ func TestV6ToV5ServerCloseEphemeralResource(t *testing.T) { t.Fatalf("unexpected error downgrading server: %s", err) } - _, err = v6server.CloseEphemeralResource(ctx, &tfprotov6.CloseEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := v6server.(tfprotov6.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("v6server should implement tfprotov6.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.CloseEphemeralResource(ctx, &tfprotov6.CloseEphemeralResourceRequest{ TypeName: "test_ephemeral_resource", }) @@ -423,7 +429,13 @@ func TestV6ToV5ServerOpenEphemeralResource(t *testing.T) { t.Fatalf("unexpected error downgrading server: %s", err) } - _, err = v6server.OpenEphemeralResource(ctx, &tfprotov6.OpenEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := v6server.(tfprotov6.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("v6server should implement tfprotov6.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.OpenEphemeralResource(ctx, &tfprotov6.OpenEphemeralResourceRequest{ TypeName: "test_ephemeral_resource", }) @@ -547,7 +559,13 @@ func TestV6ToV5ServerRenewEphemeralResource(t *testing.T) { t.Fatalf("unexpected error downgrading server: %s", err) } - _, err = v6server.RenewEphemeralResource(ctx, &tfprotov6.RenewEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := v6server.(tfprotov6.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("v6server should implement tfprotov6.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.RenewEphemeralResource(ctx, &tfprotov6.RenewEphemeralResourceRequest{ TypeName: "test_ephemeral_resource", }) @@ -669,7 +687,13 @@ func TestV6ToV5ServerValidateEphemeralResourceConfig(t *testing.T) { t.Fatalf("unexpected error downgrading server: %s", err) } - _, err = v6server.ValidateEphemeralResourceConfig(ctx, &tfprotov6.ValidateEphemeralResourceConfigRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := v6server.(tfprotov6.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("v6server should implement tfprotov6.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, &tfprotov6.ValidateEphemeralResourceConfigRequest{ TypeName: "test_resource", }) diff --git a/tf6muxserver/mux_server_CloseEphemeralResource.go b/tf6muxserver/mux_server_CloseEphemeralResource.go index 35147da..9671e35 100644 --- a/tf6muxserver/mux_server_CloseEphemeralResource.go +++ b/tf6muxserver/mux_server_CloseEphemeralResource.go @@ -28,8 +28,25 @@ func (s *muxServer) CloseEphemeralResource(ctx context.Context, req *tfprotov6.C }, nil } + // TODO: Remove and call server.CloseEphemeralResource below directly once interface becomes required. + ephemeralResourceServer, ok := server.(tfprotov6.EphemeralResourceServer) + if !ok { + resp := &tfprotov6.CloseEphemeralResourceResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "CloseEphemeralResource Not Implemented", + Detail: "A CloseEphemeralResource call was received by the provider, however the provider does not implement CloseEphemeralResource. " + + "Either upgrade the provider to a version that implements CloseEphemeralResource or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return resp, nil + } + ctx = logging.Tfprotov6ProviderServerContext(ctx, server) logging.MuxTrace(ctx, "calling downstream server") - return server.CloseEphemeralResource(ctx, req) + return ephemeralResourceServer.CloseEphemeralResource(ctx, req) } diff --git a/tf6muxserver/mux_server_CloseEphemeralResource_test.go b/tf6muxserver/mux_server_CloseEphemeralResource_test.go index c8f7cff..b0b8d13 100644 --- a/tf6muxserver/mux_server_CloseEphemeralResource_test.go +++ b/tf6muxserver/mux_server_CloseEphemeralResource_test.go @@ -38,7 +38,13 @@ func TestMuxServerCloseEphemeralResource(t *testing.T) { t.Fatalf("unexpected error setting up factory: %s", err) } - _, err = muxServer.ProviderServer().CloseEphemeralResource(ctx, &tfprotov6.CloseEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := muxServer.ProviderServer().(tfprotov6.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("muxServer should implement tfprotov6.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.CloseEphemeralResource(ctx, &tfprotov6.CloseEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server1", }) @@ -54,7 +60,7 @@ func TestMuxServerCloseEphemeralResource(t *testing.T) { t.Errorf("unexpected test_ephemeral_resource_server1 CloseEphemeralResource called on server2") } - _, err = muxServer.ProviderServer().CloseEphemeralResource(ctx, &tfprotov6.CloseEphemeralResourceRequest{ + _, err = ephemeralResourceServer.CloseEphemeralResource(ctx, &tfprotov6.CloseEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server2", }) diff --git a/tf6muxserver/mux_server_OpenEphemeralResource.go b/tf6muxserver/mux_server_OpenEphemeralResource.go index ff54ff1..8576473 100644 --- a/tf6muxserver/mux_server_OpenEphemeralResource.go +++ b/tf6muxserver/mux_server_OpenEphemeralResource.go @@ -28,8 +28,25 @@ func (s *muxServer) OpenEphemeralResource(ctx context.Context, req *tfprotov6.Op }, nil } + // TODO: Remove and call server.OpenEphemeralResource below directly once interface becomes required. + ephemeralResourceServer, ok := server.(tfprotov6.EphemeralResourceServer) + if !ok { + resp := &tfprotov6.OpenEphemeralResourceResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "OpenEphemeralResource Not Implemented", + Detail: "A OpenEphemeralResource call was received by the provider, however the provider does not implement OpenEphemeralResource. " + + "Either upgrade the provider to a version that implements OpenEphemeralResource or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return resp, nil + } + ctx = logging.Tfprotov6ProviderServerContext(ctx, server) logging.MuxTrace(ctx, "calling downstream server") - return server.OpenEphemeralResource(ctx, req) + return ephemeralResourceServer.OpenEphemeralResource(ctx, req) } diff --git a/tf6muxserver/mux_server_OpenEphemeralResource_test.go b/tf6muxserver/mux_server_OpenEphemeralResource_test.go index 80ccd5f..2c887e8 100644 --- a/tf6muxserver/mux_server_OpenEphemeralResource_test.go +++ b/tf6muxserver/mux_server_OpenEphemeralResource_test.go @@ -38,7 +38,13 @@ func TestMuxServerOpenEphemeralResource(t *testing.T) { t.Fatalf("unexpected error setting up factory: %s", err) } - _, err = muxServer.ProviderServer().OpenEphemeralResource(ctx, &tfprotov6.OpenEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := muxServer.ProviderServer().(tfprotov6.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("muxServer should implement tfprotov6.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.OpenEphemeralResource(ctx, &tfprotov6.OpenEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server1", }) @@ -54,7 +60,7 @@ func TestMuxServerOpenEphemeralResource(t *testing.T) { t.Errorf("unexpected test_ephemeral_resource_server1 OpenEphemeralResource called on server2") } - _, err = muxServer.ProviderServer().OpenEphemeralResource(ctx, &tfprotov6.OpenEphemeralResourceRequest{ + _, err = ephemeralResourceServer.OpenEphemeralResource(ctx, &tfprotov6.OpenEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server2", }) diff --git a/tf6muxserver/mux_server_RenewEphemeralResource.go b/tf6muxserver/mux_server_RenewEphemeralResource.go index 26156dc..322baa4 100644 --- a/tf6muxserver/mux_server_RenewEphemeralResource.go +++ b/tf6muxserver/mux_server_RenewEphemeralResource.go @@ -28,8 +28,25 @@ func (s *muxServer) RenewEphemeralResource(ctx context.Context, req *tfprotov6.R }, nil } + // TODO: Remove and call server.RenewEphemeralResource below directly once interface becomes required. + ephemeralResourceServer, ok := server.(tfprotov6.EphemeralResourceServer) + if !ok { + resp := &tfprotov6.RenewEphemeralResourceResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "RenewEphemeralResource Not Implemented", + Detail: "A RenewEphemeralResource call was received by the provider, however the provider does not implement RenewEphemeralResource. " + + "Either upgrade the provider to a version that implements RenewEphemeralResource or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return resp, nil + } + ctx = logging.Tfprotov6ProviderServerContext(ctx, server) logging.MuxTrace(ctx, "calling downstream server") - return server.RenewEphemeralResource(ctx, req) + return ephemeralResourceServer.RenewEphemeralResource(ctx, req) } diff --git a/tf6muxserver/mux_server_RenewEphemeralResource_test.go b/tf6muxserver/mux_server_RenewEphemeralResource_test.go index 090f6cf..5bdb67d 100644 --- a/tf6muxserver/mux_server_RenewEphemeralResource_test.go +++ b/tf6muxserver/mux_server_RenewEphemeralResource_test.go @@ -38,7 +38,13 @@ func TestMuxServerRenewEphemeralResource(t *testing.T) { t.Fatalf("unexpected error setting up factory: %s", err) } - _, err = muxServer.ProviderServer().RenewEphemeralResource(ctx, &tfprotov6.RenewEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := muxServer.ProviderServer().(tfprotov6.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("muxServer should implement tfprotov6.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.RenewEphemeralResource(ctx, &tfprotov6.RenewEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server1", }) @@ -54,7 +60,7 @@ func TestMuxServerRenewEphemeralResource(t *testing.T) { t.Errorf("unexpected test_ephemeral_resource_server1 RenewEphemeralResource called on server2") } - _, err = muxServer.ProviderServer().RenewEphemeralResource(ctx, &tfprotov6.RenewEphemeralResourceRequest{ + _, err = ephemeralResourceServer.RenewEphemeralResource(ctx, &tfprotov6.RenewEphemeralResourceRequest{ TypeName: "test_ephemeral_resource_server2", }) diff --git a/tf6muxserver/mux_server_ValidateEphemeralResourceConfig.go b/tf6muxserver/mux_server_ValidateEphemeralResourceConfig.go index aac483d..4ee5146 100644 --- a/tf6muxserver/mux_server_ValidateEphemeralResourceConfig.go +++ b/tf6muxserver/mux_server_ValidateEphemeralResourceConfig.go @@ -28,8 +28,25 @@ func (s *muxServer) ValidateEphemeralResourceConfig(ctx context.Context, req *tf }, nil } + // TODO: Remove and call server.ValidateEphemeralResourceConfig below directly once interface becomes required. + ephemeralResourceServer, ok := server.(tfprotov6.EphemeralResourceServer) + if !ok { + resp := &tfprotov6.ValidateEphemeralResourceConfigResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "ValidateEphemeralResourceConfig Not Implemented", + Detail: "A ValidateEphemeralResourceConfig call was received by the provider, however the provider does not implement ValidateEphemeralResourceConfig. " + + "Either upgrade the provider to a version that implements ValidateEphemeralResourceConfig or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return resp, nil + } + ctx = logging.Tfprotov6ProviderServerContext(ctx, server) logging.MuxTrace(ctx, "calling downstream server") - return server.ValidateEphemeralResourceConfig(ctx, req) + return ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, req) } diff --git a/tf6muxserver/mux_server_ValidateEphemeralResourceConfig_test.go b/tf6muxserver/mux_server_ValidateEphemeralResourceConfig_test.go index 8df2240..424349f 100644 --- a/tf6muxserver/mux_server_ValidateEphemeralResourceConfig_test.go +++ b/tf6muxserver/mux_server_ValidateEphemeralResourceConfig_test.go @@ -38,7 +38,13 @@ func TestMuxServerValidateEphemeralResourceConfig(t *testing.T) { t.Fatalf("unexpected error setting up factory: %s", err) } - _, err = muxServer.ProviderServer().ValidateEphemeralResourceConfig(ctx, &tfprotov6.ValidateEphemeralResourceConfigRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := muxServer.ProviderServer().(tfprotov6.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("muxServer should implement tfprotov6.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, &tfprotov6.ValidateEphemeralResourceConfigRequest{ TypeName: "test_ephemeral_resource_server1", }) @@ -54,7 +60,7 @@ func TestMuxServerValidateEphemeralResourceConfig(t *testing.T) { t.Errorf("unexpected test_ephemeral_resource_server1 ValidateEphemeralResourceConfig called on server2") } - _, err = muxServer.ProviderServer().ValidateEphemeralResourceConfig(ctx, &tfprotov6.ValidateEphemeralResourceConfigRequest{ + _, err = ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, &tfprotov6.ValidateEphemeralResourceConfigRequest{ TypeName: "test_ephemeral_resource_server2", }) diff --git a/tf6to5server/tf6to5server.go b/tf6to5server/tf6to5server.go index c4dcd2d..de724c7 100644 --- a/tf6to5server/tf6to5server.go +++ b/tf6to5server/tf6to5server.go @@ -68,9 +68,27 @@ func (s v6tov5Server) CallFunction(ctx context.Context, req *tfprotov5.CallFunct } func (s v6tov5Server) CloseEphemeralResource(ctx context.Context, req *tfprotov5.CloseEphemeralResourceRequest) (*tfprotov5.CloseEphemeralResourceResponse, error) { + // TODO: Remove and call s.v6Server.CloseEphemeralResource below directly once interface becomes required + ephemeralResourceServer, ok := s.v6Server.(tfprotov6.EphemeralResourceServer) + if !ok { + v5Resp := &tfprotov5.CloseEphemeralResourceResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "CloseEphemeralResource Not Implemented", + Detail: "A CloseEphemeralResource call was received by the provider, however the provider does not implement the RPC. " + + "Either upgrade the provider to a version that implements CloseEphemeralResource or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return v5Resp, nil + } + v6Req := tfprotov5tov6.CloseEphemeralResourceRequest(req) - v6Resp, err := s.v6Server.CloseEphemeralResource(ctx, v6Req) + // v6Resp, err := s.v6Server.CloseEphemeralResource(ctx, v6Req) + v6Resp, err := ephemeralResourceServer.CloseEphemeralResource(ctx, v6Req) if err != nil { return nil, err } @@ -145,9 +163,27 @@ func (s v6tov5Server) MoveResourceState(ctx context.Context, req *tfprotov5.Move } func (s v6tov5Server) OpenEphemeralResource(ctx context.Context, req *tfprotov5.OpenEphemeralResourceRequest) (*tfprotov5.OpenEphemeralResourceResponse, error) { + // TODO: Remove and call s.v6Server.OpenEphemeralResource below directly once interface becomes required + ephemeralResourceServer, ok := s.v6Server.(tfprotov6.EphemeralResourceServer) + if !ok { + v5Resp := &tfprotov5.OpenEphemeralResourceResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "OpenEphemeralResource Not Implemented", + Detail: "A OpenEphemeralResource call was received by the provider, however the provider does not implement the RPC. " + + "Either upgrade the provider to a version that implements OpenEphemeralResource or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return v5Resp, nil + } + v6Req := tfprotov5tov6.OpenEphemeralResourceRequest(req) - v6Resp, err := s.v6Server.OpenEphemeralResource(ctx, v6Req) + // v6Resp, err := s.v6Server.OpenEphemeralResource(ctx, v6Req) + v6Resp, err := ephemeralResourceServer.OpenEphemeralResource(ctx, v6Req) if err != nil { return nil, err } @@ -205,9 +241,27 @@ func (s v6tov5Server) ReadResource(ctx context.Context, req *tfprotov5.ReadResou } func (s v6tov5Server) RenewEphemeralResource(ctx context.Context, req *tfprotov5.RenewEphemeralResourceRequest) (*tfprotov5.RenewEphemeralResourceResponse, error) { + // TODO: Remove and call s.v6Server.RenewEphemeralResource below directly once interface becomes required + ephemeralResourceServer, ok := s.v6Server.(tfprotov6.EphemeralResourceServer) + if !ok { + v5Resp := &tfprotov5.RenewEphemeralResourceResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "RenewEphemeralResource Not Implemented", + Detail: "A RenewEphemeralResource call was received by the provider, however the provider does not implement the RPC. " + + "Either upgrade the provider to a version that implements RenewEphemeralResource or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return v5Resp, nil + } + v6Req := tfprotov5tov6.RenewEphemeralResourceRequest(req) - v6Resp, err := s.v6Server.RenewEphemeralResource(ctx, v6Req) + // v6Resp, err := s.v6Server.RenewEphemeralResource(ctx, v6Req) + v6Resp, err := ephemeralResourceServer.RenewEphemeralResource(ctx, v6Req) if err != nil { return nil, err } @@ -249,9 +303,27 @@ func (s v6tov5Server) ValidateDataSourceConfig(ctx context.Context, req *tfproto } func (s v6tov5Server) ValidateEphemeralResourceConfig(ctx context.Context, req *tfprotov5.ValidateEphemeralResourceConfigRequest) (*tfprotov5.ValidateEphemeralResourceConfigResponse, error) { + // TODO: Remove and call s.v6Server.ValidateEphemeralResourceConfig below directly once interface becomes required + ephemeralResourceServer, ok := s.v6Server.(tfprotov6.EphemeralResourceServer) + if !ok { + v5Resp := &tfprotov5.ValidateEphemeralResourceConfigResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "ValidateEphemeralResourceConfig Not Implemented", + Detail: "A ValidateEphemeralResourceConfig call was received by the provider, however the provider does not implement the RPC. " + + "Either upgrade the provider to a version that implements ValidateEphemeralResourceConfig or this is a bug in Terraform that should be reported to the Terraform maintainers.", + }, + }, + } + + return v5Resp, nil + } + v6Req := tfprotov5tov6.ValidateEphemeralResourceConfigRequest(req) - v6Resp, err := s.v6Server.ValidateEphemeralResourceConfig(ctx, v6Req) + // v6Resp, err := s.v6Server.ValidateEphemeralResourceConfig(ctx, v6Req) + v6Resp, err := ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, v6Req) if err != nil { return nil, err } diff --git a/tf6to5server/tf6to5server_test.go b/tf6to5server/tf6to5server_test.go index 4977d8f..5b9e9f5 100644 --- a/tf6to5server/tf6to5server_test.go +++ b/tf6to5server/tf6to5server_test.go @@ -310,7 +310,13 @@ func TestV6ToV5ServerCloseEphemeralResource(t *testing.T) { t.Fatalf("unexpected error downgrading server: %s", err) } - _, err = v5server.CloseEphemeralResource(ctx, &tfprotov5.CloseEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := v5server.(tfprotov5.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("v5server should implement tfprotov5.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.CloseEphemeralResource(ctx, &tfprotov5.CloseEphemeralResourceRequest{ TypeName: "test_ephemeral_resource", }) @@ -521,7 +527,13 @@ func TestV6ToV5ServerOpenEphemeralResource(t *testing.T) { t.Fatalf("unexpected error downgrading server: %s", err) } - _, err = v5server.OpenEphemeralResource(ctx, &tfprotov5.OpenEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := v5server.(tfprotov5.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("v5server should implement tfprotov5.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.OpenEphemeralResource(ctx, &tfprotov5.OpenEphemeralResourceRequest{ TypeName: "test_ephemeral_resource", }) @@ -674,7 +686,13 @@ func TestV6ToV5ServerRenewEphemeralResource(t *testing.T) { t.Fatalf("unexpected error downgrading server: %s", err) } - _, err = v5server.RenewEphemeralResource(ctx, &tfprotov5.RenewEphemeralResourceRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := v5server.(tfprotov5.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("v5server should implement tfprotov5.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.RenewEphemeralResource(ctx, &tfprotov5.RenewEphemeralResourceRequest{ TypeName: "test_ephemeral_resource", }) @@ -796,7 +814,13 @@ func TestV6ToV5ServerValidateEphemeralResourceConfig(t *testing.T) { t.Fatalf("unexpected error downgrading server: %s", err) } - _, err = v5server.ValidateEphemeralResourceConfig(ctx, &tfprotov5.ValidateEphemeralResourceConfigRequest{ + //nolint:staticcheck // Intentionally verifying interface implementation + ephemeralResourceServer, ok := v5server.(tfprotov5.ProviderServerWithEphemeralResources) + if !ok { + t.Fatal("v5server should implement tfprotov5.ProviderServerWithEphemeralResources") + } + + _, err = ephemeralResourceServer.ValidateEphemeralResourceConfig(ctx, &tfprotov5.ValidateEphemeralResourceConfigRequest{ TypeName: "test_ephemeral_resource", }) From 1353f07c676265dfd7f4e70fc943cf06349fee4d Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Tue, 24 Sep 2024 11:53:00 -0400 Subject: [PATCH 05/10] remove `config` from renew request --- go.mod | 12 +++++----- go.sum | 24 ++++++++++---------- internal/tfprotov5tov6/tfprotov5tov6.go | 1 - internal/tfprotov5tov6/tfprotov5tov6_test.go | 2 -- internal/tfprotov6tov5/tfprotov6tov5.go | 1 - internal/tfprotov6tov5/tfprotov6tov5_test.go | 2 -- 6 files changed, 18 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index ded2c9f..ba7c21f 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ toolchain go1.22.7 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-go v0.24.0 + github.com/hashicorp/terraform-plugin-go v0.24.1-0.20240924154457-cd3b6654adf0 github.com/hashicorp/terraform-plugin-log v0.9.0 - google.golang.org/grpc v1.66.2 + google.golang.org/grpc v1.67.0 ) require ( @@ -26,9 +26,9 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/protobuf v1.34.2 // indirect ) diff --git a/go.sum b/go.sum index 3fe9c73..4ea1430 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOs github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= 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.24.0 h1:2WpHhginCdVhFIrWHxDEg6RBn3YaWzR2o6qUeIEat2U= -github.com/hashicorp/terraform-plugin-go v0.24.0/go.mod h1:tUQ53lAsOyYSckFGEefGC5C8BAaO0ENqzFd3bQeuYQg= +github.com/hashicorp/terraform-plugin-go v0.24.1-0.20240924154457-cd3b6654adf0 h1:WgAlLzllo3v6BeZccPW5IETHgRylr/9y37X314C+09I= +github.com/hashicorp/terraform-plugin-go v0.24.1-0.20240924154457-cd3b6654adf0/go.mod h1:zoSM9LyEFI4iVkDeKXgwBHV0uTuIIXydtK1fy9J4wBA= 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.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= @@ -46,21 +46,21 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= -google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= +google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/tfprotov5tov6/tfprotov5tov6.go b/internal/tfprotov5tov6/tfprotov5tov6.go index 0517e75..852e636 100644 --- a/internal/tfprotov5tov6/tfprotov5tov6.go +++ b/internal/tfprotov5tov6/tfprotov5tov6.go @@ -607,7 +607,6 @@ func RenewEphemeralResourceRequest(in *tfprotov5.RenewEphemeralResourceRequest) return &tfprotov6.RenewEphemeralResourceRequest{ TypeName: in.TypeName, - Config: DynamicValue(in.Config), State: DynamicValue(in.State), Private: in.Private, } diff --git a/internal/tfprotov5tov6/tfprotov5tov6_test.go b/internal/tfprotov5tov6/tfprotov5tov6_test.go index 449fcad..7613d0d 100644 --- a/internal/tfprotov5tov6/tfprotov5tov6_test.go +++ b/internal/tfprotov5tov6/tfprotov5tov6_test.go @@ -1799,12 +1799,10 @@ func TestRenewEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov5.RenewEphemeralResourceRequest{ - Config: &testTfprotov5DynamicValue, State: &testTfprotov5DynamicValue, TypeName: "test_ephemeral_resource", }, expected: &tfprotov6.RenewEphemeralResourceRequest{ - Config: &testTfprotov6DynamicValue, State: &testTfprotov6DynamicValue, TypeName: "test_ephemeral_resource", }, diff --git a/internal/tfprotov6tov5/tfprotov6tov5.go b/internal/tfprotov6tov5/tfprotov6tov5.go index f99aaf7..b86fe88 100644 --- a/internal/tfprotov6tov5/tfprotov6tov5.go +++ b/internal/tfprotov6tov5/tfprotov6tov5.go @@ -661,7 +661,6 @@ func RenewEphemeralResourceRequest(in *tfprotov6.RenewEphemeralResourceRequest) return &tfprotov5.RenewEphemeralResourceRequest{ TypeName: in.TypeName, - Config: DynamicValue(in.Config), State: DynamicValue(in.State), Private: in.Private, } diff --git a/internal/tfprotov6tov5/tfprotov6tov5_test.go b/internal/tfprotov6tov5/tfprotov6tov5_test.go index 437b39f..bbadd71 100644 --- a/internal/tfprotov6tov5/tfprotov6tov5_test.go +++ b/internal/tfprotov6tov5/tfprotov6tov5_test.go @@ -2014,12 +2014,10 @@ func TestRenewEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov6.RenewEphemeralResourceRequest{ - Config: &testTfprotov6DynamicValue, State: &testTfprotov6DynamicValue, TypeName: "test_ephemeral_resource", }, expected: &tfprotov5.RenewEphemeralResourceRequest{ - Config: &testTfprotov5DynamicValue, State: &testTfprotov5DynamicValue, TypeName: "test_ephemeral_resource", }, From 6d925b3289528092d2a7863c0571ab176d0c2066 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Thu, 3 Oct 2024 16:58:59 -0400 Subject: [PATCH 06/10] remove state from close/renew + add deferrals --- go.mod | 4 +- go.sum | 8 ++-- internal/tfprotov5tov6/tfprotov5tov6.go | 23 +++++++--- internal/tfprotov5tov6/tfprotov5tov6_test.go | 44 ++++++++++++++++---- internal/tfprotov6tov5/tfprotov6tov5.go | 23 +++++++--- internal/tfprotov6tov5/tfprotov6tov5_test.go | 44 ++++++++++++++++---- 6 files changed, 112 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index ba7c21f..0ca2ebd 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ toolchain go1.22.7 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-go v0.24.1-0.20240924154457-cd3b6654adf0 + github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241003195015-94a6f8ce9ce7 github.com/hashicorp/terraform-plugin-log v0.9.0 - google.golang.org/grpc v1.67.0 + google.golang.org/grpc v1.67.1 ) require ( diff --git a/go.sum b/go.sum index 4ea1430..1fc9933 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOs github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= 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.24.1-0.20240924154457-cd3b6654adf0 h1:WgAlLzllo3v6BeZccPW5IETHgRylr/9y37X314C+09I= -github.com/hashicorp/terraform-plugin-go v0.24.1-0.20240924154457-cd3b6654adf0/go.mod h1:zoSM9LyEFI4iVkDeKXgwBHV0uTuIIXydtK1fy9J4wBA= +github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241003195015-94a6f8ce9ce7 h1:Qrnz5z9gKKn7oDhWNKPLzl7yygiz322p1Rkzx5uqLiA= +github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241003195015-94a6f8ce9ce7/go.mod h1:lmP6B7frH0MrScpBN0x9kY8DSFSUWX54NmeL00knT9c= 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.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= @@ -59,8 +59,8 @@ golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= -google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/tfprotov5tov6/tfprotov5tov6.go b/internal/tfprotov5tov6/tfprotov5tov6.go index 852e636..faf8fbd 100644 --- a/internal/tfprotov5tov6/tfprotov5tov6.go +++ b/internal/tfprotov5tov6/tfprotov5tov6.go @@ -71,7 +71,6 @@ func CloseEphemeralResourceRequest(in *tfprotov5.CloseEphemeralResourceRequest) return &tfprotov6.CloseEphemeralResourceRequest{ TypeName: in.TypeName, - State: DynamicValue(in.State), Private: in.Private, } } @@ -452,9 +451,22 @@ func OpenEphemeralResourceRequest(in *tfprotov5.OpenEphemeralResourceRequest) *t } return &tfprotov6.OpenEphemeralResourceRequest{ - TypeName: in.TypeName, - Config: DynamicValue(in.Config), + TypeName: in.TypeName, + Config: DynamicValue(in.Config), + ClientCapabilities: OpenEphemeralResourceClientCapabilities(in.ClientCapabilities), + } +} + +func OpenEphemeralResourceClientCapabilities(in *tfprotov5.OpenEphemeralResourceClientCapabilities) *tfprotov6.OpenEphemeralResourceClientCapabilities { + if in == nil { + return nil + } + + resp := &tfprotov6.OpenEphemeralResourceClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, } + + return resp } func OpenEphemeralResourceResponse(in *tfprotov5.OpenEphemeralResourceResponse) *tfprotov6.OpenEphemeralResourceResponse { @@ -463,11 +475,11 @@ func OpenEphemeralResourceResponse(in *tfprotov5.OpenEphemeralResourceResponse) } return &tfprotov6.OpenEphemeralResourceResponse{ - State: DynamicValue(in.State), + Result: DynamicValue(in.Result), Diagnostics: Diagnostics(in.Diagnostics), Private: in.Private, RenewAt: in.RenewAt, - IsClosable: in.IsClosable, + Deferred: Deferred(in.Deferred), } } @@ -607,7 +619,6 @@ func RenewEphemeralResourceRequest(in *tfprotov5.RenewEphemeralResourceRequest) return &tfprotov6.RenewEphemeralResourceRequest{ TypeName: in.TypeName, - State: DynamicValue(in.State), Private: in.Private, } } diff --git a/internal/tfprotov5tov6/tfprotov5tov6_test.go b/internal/tfprotov5tov6/tfprotov5tov6_test.go index 7613d0d..48b18a3 100644 --- a/internal/tfprotov5tov6/tfprotov5tov6_test.go +++ b/internal/tfprotov5tov6/tfprotov5tov6_test.go @@ -306,12 +306,10 @@ func TestCloseEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov5.CloseEphemeralResourceRequest{ - State: &testTfprotov5DynamicValue, Private: testBytes, TypeName: "test_ephemeral_resource", }, expected: &tfprotov6.CloseEphemeralResourceRequest{ - State: &testTfprotov6DynamicValue, Private: testBytes, TypeName: "test_ephemeral_resource", }, @@ -1311,6 +1309,22 @@ func TestOpenEphemeralResourceRequest(t *testing.T) { TypeName: "test_ephemeral_resource", }, }, + "client-capabilities-deferral-allowed": { + in: &tfprotov5.OpenEphemeralResourceRequest{ + Config: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", + ClientCapabilities: &tfprotov5.OpenEphemeralResourceClientCapabilities{ + DeferralAllowed: true, + }, + }, + expected: &tfprotov6.OpenEphemeralResourceRequest{ + Config: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", + ClientCapabilities: &tfprotov6.OpenEphemeralResourceClientCapabilities{ + DeferralAllowed: true, + }, + }, + }, } for name, testCase := range testCases { @@ -1342,17 +1356,31 @@ func TestOpenEphemeralResourceResponse(t *testing.T) { "all-valid-fields": { in: &tfprotov5.OpenEphemeralResourceResponse{ Diagnostics: testTfprotov5Diagnostics, - IsClosable: true, Private: testBytes, RenewAt: testTime, - State: &testTfprotov5DynamicValue, + Result: &testTfprotov5DynamicValue, }, expected: &tfprotov6.OpenEphemeralResourceResponse{ Diagnostics: testTfprotov6Diagnostics, - IsClosable: true, Private: testBytes, RenewAt: testTime, - State: &testTfprotov6DynamicValue, + Result: &testTfprotov6DynamicValue, + }, + }, + "deferred-reason": { + in: &tfprotov5.OpenEphemeralResourceResponse{ + Diagnostics: testTfprotov5Diagnostics, + Result: &testTfprotov5DynamicValue, + Deferred: &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReasonResourceConfigUnknown, + }, + }, + expected: &tfprotov6.OpenEphemeralResourceResponse{ + Diagnostics: testTfprotov6Diagnostics, + Result: &testTfprotov6DynamicValue, + Deferred: &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReasonResourceConfigUnknown, + }, }, }, } @@ -1799,11 +1827,11 @@ func TestRenewEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov5.RenewEphemeralResourceRequest{ - State: &testTfprotov5DynamicValue, + Private: testBytes, TypeName: "test_ephemeral_resource", }, expected: &tfprotov6.RenewEphemeralResourceRequest{ - State: &testTfprotov6DynamicValue, + Private: testBytes, TypeName: "test_ephemeral_resource", }, }, diff --git a/internal/tfprotov6tov5/tfprotov6tov5.go b/internal/tfprotov6tov5/tfprotov6tov5.go index b86fe88..1db72cd 100644 --- a/internal/tfprotov6tov5/tfprotov6tov5.go +++ b/internal/tfprotov6tov5/tfprotov6tov5.go @@ -76,7 +76,6 @@ func CloseEphemeralResourceRequest(in *tfprotov6.CloseEphemeralResourceRequest) return &tfprotov5.CloseEphemeralResourceRequest{ TypeName: in.TypeName, - State: DynamicValue(in.State), Private: in.Private, } } @@ -486,9 +485,22 @@ func OpenEphemeralResourceRequest(in *tfprotov6.OpenEphemeralResourceRequest) *t } return &tfprotov5.OpenEphemeralResourceRequest{ - TypeName: in.TypeName, - Config: DynamicValue(in.Config), + TypeName: in.TypeName, + Config: DynamicValue(in.Config), + ClientCapabilities: OpenEphemeralResourceClientCapabilities(in.ClientCapabilities), + } +} + +func OpenEphemeralResourceClientCapabilities(in *tfprotov6.OpenEphemeralResourceClientCapabilities) *tfprotov5.OpenEphemeralResourceClientCapabilities { + if in == nil { + return nil + } + + resp := &tfprotov5.OpenEphemeralResourceClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, } + + return resp } func OpenEphemeralResourceResponse(in *tfprotov6.OpenEphemeralResourceResponse) *tfprotov5.OpenEphemeralResourceResponse { @@ -497,11 +509,11 @@ func OpenEphemeralResourceResponse(in *tfprotov6.OpenEphemeralResourceResponse) } return &tfprotov5.OpenEphemeralResourceResponse{ - State: DynamicValue(in.State), + Result: DynamicValue(in.Result), Diagnostics: Diagnostics(in.Diagnostics), Private: in.Private, RenewAt: in.RenewAt, - IsClosable: in.IsClosable, + Deferred: Deferred(in.Deferred), } } @@ -661,7 +673,6 @@ func RenewEphemeralResourceRequest(in *tfprotov6.RenewEphemeralResourceRequest) return &tfprotov5.RenewEphemeralResourceRequest{ TypeName: in.TypeName, - State: DynamicValue(in.State), Private: in.Private, } } diff --git a/internal/tfprotov6tov5/tfprotov6tov5_test.go b/internal/tfprotov6tov5/tfprotov6tov5_test.go index bbadd71..e170f2c 100644 --- a/internal/tfprotov6tov5/tfprotov6tov5_test.go +++ b/internal/tfprotov6tov5/tfprotov6tov5_test.go @@ -308,12 +308,10 @@ func TestCloseEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov6.CloseEphemeralResourceRequest{ - State: &testTfprotov6DynamicValue, Private: testBytes, TypeName: "test_ephemeral_resource", }, expected: &tfprotov5.CloseEphemeralResourceRequest{ - State: &testTfprotov5DynamicValue, Private: testBytes, TypeName: "test_ephemeral_resource", }, @@ -1452,6 +1450,22 @@ func TestOpenEphemeralResourceRequest(t *testing.T) { TypeName: "test_ephemeral_resource", }, }, + "client-capabilities-deferral-allowed": { + in: &tfprotov6.OpenEphemeralResourceRequest{ + Config: &testTfprotov6DynamicValue, + TypeName: "test_ephemeral_resource", + ClientCapabilities: &tfprotov6.OpenEphemeralResourceClientCapabilities{ + DeferralAllowed: true, + }, + }, + expected: &tfprotov5.OpenEphemeralResourceRequest{ + Config: &testTfprotov5DynamicValue, + TypeName: "test_ephemeral_resource", + ClientCapabilities: &tfprotov5.OpenEphemeralResourceClientCapabilities{ + DeferralAllowed: true, + }, + }, + }, } for name, testCase := range testCases { @@ -1483,17 +1497,31 @@ func TestOpenEphemeralResourceResponse(t *testing.T) { "all-valid-fields": { in: &tfprotov6.OpenEphemeralResourceResponse{ Diagnostics: testTfprotov6Diagnostics, - IsClosable: true, Private: testBytes, RenewAt: testTime, - State: &testTfprotov6DynamicValue, + Result: &testTfprotov6DynamicValue, }, expected: &tfprotov5.OpenEphemeralResourceResponse{ Diagnostics: testTfprotov5Diagnostics, - IsClosable: true, Private: testBytes, RenewAt: testTime, - State: &testTfprotov5DynamicValue, + Result: &testTfprotov5DynamicValue, + }, + }, + "deferred-reason": { + in: &tfprotov6.OpenEphemeralResourceResponse{ + Diagnostics: testTfprotov6Diagnostics, + Result: &testTfprotov6DynamicValue, + Deferred: &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReasonResourceConfigUnknown, + }, + }, + expected: &tfprotov5.OpenEphemeralResourceResponse{ + Diagnostics: testTfprotov5Diagnostics, + Result: &testTfprotov5DynamicValue, + Deferred: &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReasonResourceConfigUnknown, + }, }, }, } @@ -2014,11 +2042,11 @@ func TestRenewEphemeralResourceRequest(t *testing.T) { }, "all-valid-fields": { in: &tfprotov6.RenewEphemeralResourceRequest{ - State: &testTfprotov6DynamicValue, + Private: testBytes, TypeName: "test_ephemeral_resource", }, expected: &tfprotov5.RenewEphemeralResourceRequest{ - State: &testTfprotov5DynamicValue, + Private: testBytes, TypeName: "test_ephemeral_resource", }, }, From 0f3eef8d5ee98a43b64f243383c719a272973e25 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 28 Oct 2024 16:39:13 -0400 Subject: [PATCH 07/10] Update `terraform-plugin-go` dependency --- go.mod | 8 ++++---- go.sum | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 0ca2ebd..245c296 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.7 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241003195015-94a6f8ce9ce7 + github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241028202921-4f77f7c47924 github.com/hashicorp/terraform-plugin-log v0.9.0 google.golang.org/grpc v1.67.1 ) @@ -15,13 +15,13 @@ require ( github.com/fatih/color v1.13.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect - github.com/hashicorp/go-plugin v1.6.1 // indirect + github.com/hashicorp/go-plugin v1.6.2 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/oklog/run v1.0.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect @@ -30,5 +30,5 @@ require ( golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.17.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/protobuf v1.35.1 // indirect ) diff --git a/go.sum b/go.sum index 1fc9933..48b0895 100644 --- a/go.sum +++ b/go.sum @@ -13,10 +13,13 @@ github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+ github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= +github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= 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.24.1-0.20241003195015-94a6f8ce9ce7 h1:Qrnz5z9gKKn7oDhWNKPLzl7yygiz322p1Rkzx5uqLiA= github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241003195015-94a6f8ce9ce7/go.mod h1:lmP6B7frH0MrScpBN0x9kY8DSFSUWX54NmeL00knT9c= +github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241028202921-4f77f7c47924 h1:mRX1qbgQ+2RMZ+MirJCZ4sgfhoxzVzkFyUcEKw2NWh0= +github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241028202921-4f77f7c47924/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw= 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.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= @@ -33,6 +36,7 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= @@ -53,6 +57,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= @@ -63,6 +68,7 @@ google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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= From 410ebab956c0137b91def281a31f128cc8820cc6 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 28 Oct 2024 16:48:38 -0400 Subject: [PATCH 08/10] Run `go mod tidy` --- go.sum | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/go.sum b/go.sum index 48b0895..2dcc994 100644 --- a/go.sum +++ b/go.sum @@ -11,13 +11,10 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= -github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= +github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog= github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= 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.24.1-0.20241003195015-94a6f8ce9ce7 h1:Qrnz5z9gKKn7oDhWNKPLzl7yygiz322p1Rkzx5uqLiA= -github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241003195015-94a6f8ce9ce7/go.mod h1:lmP6B7frH0MrScpBN0x9kY8DSFSUWX54NmeL00knT9c= github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241028202921-4f77f7c47924 h1:mRX1qbgQ+2RMZ+MirJCZ4sgfhoxzVzkFyUcEKw2NWh0= github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241028202921-4f77f7c47924/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= @@ -34,8 +31,8 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= @@ -44,8 +41,9 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= @@ -66,8 +64,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From a0c2f4368898bd868d944b816b8eadcbfa2fc6f7 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 28 Oct 2024 16:51:55 -0400 Subject: [PATCH 09/10] Add changelog entry --- .changes/unreleased/FEATURES-20241028-164944.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changes/unreleased/FEATURES-20241028-164944.yaml diff --git a/.changes/unreleased/FEATURES-20241028-164944.yaml b/.changes/unreleased/FEATURES-20241028-164944.yaml new file mode 100644 index 0000000..f7428df --- /dev/null +++ b/.changes/unreleased/FEATURES-20241028-164944.yaml @@ -0,0 +1,5 @@ +kind: FEATURES +body: 'all: Upgrade protocol versions to support ephemeral resource types' +time: 2024-10-28T16:49:44.46102-04:00 +custom: + Issue: "257" From a8a4cff5c615192687e5f9616199cb8c3d3a743f Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Wed, 30 Oct 2024 09:16:20 -0400 Subject: [PATCH 10/10] Upgrade `terraform-plugin-go` dependency to `v0.25.0` --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 245c296..20f2ff7 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.7 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241028202921-4f77f7c47924 + github.com/hashicorp/terraform-plugin-go v0.25.0 github.com/hashicorp/terraform-plugin-log v0.9.0 google.golang.org/grpc v1.67.1 ) diff --git a/go.sum b/go.sum index 2dcc994..99e7874 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8Ei github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= 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.24.1-0.20241028202921-4f77f7c47924 h1:mRX1qbgQ+2RMZ+MirJCZ4sgfhoxzVzkFyUcEKw2NWh0= -github.com/hashicorp/terraform-plugin-go v0.24.1-0.20241028202921-4f77f7c47924/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw= +github.com/hashicorp/terraform-plugin-go v0.25.0 h1:oi13cx7xXA6QciMcpcFi/rwA974rdTxjqEhXJjbAyks= +github.com/hashicorp/terraform-plugin-go v0.25.0/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw= 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.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI=